fix tests + flyer upload (anon)
Some checks failed
Deploy to Test Environment / deploy-to-test (push) Failing after 42s
Some checks failed
Deploy to Test Environment / deploy-to-test (push) Failing after 42s
This commit is contained in:
@@ -185,7 +185,17 @@ jobs:
|
||||
- name: Show PM2 Environment for Production
|
||||
run: |
|
||||
echo "--- Displaying recent PM2 logs for flyer-crawler-api ---"
|
||||
sleep 5
|
||||
pm2 describe flyer-crawler-api || echo "Could not find production pm2 process."
|
||||
pm2 logs flyer-crawler-api --lines 20 --nostream || echo "Could not find production pm2 process."
|
||||
pm2 env flyer-crawler-api || echo "Could not find production pm2 process."
|
||||
sleep 5 # Wait a few seconds for the app to start and log its output.
|
||||
|
||||
# Resolve the PM2 ID dynamically to ensure we target the correct process
|
||||
PM2_ID=$(pm2 jlist | node -e "try { const list = JSON.parse(require('fs').readFileSync(0, 'utf-8')); const app = list.find(p => p.name === 'flyer-crawler-api'); console.log(app ? app.pm2_env.pm_id : ''); } catch(e) { console.log(''); }")
|
||||
|
||||
if [ -n "$PM2_ID" ]; then
|
||||
echo "Found process ID: $PM2_ID"
|
||||
pm2 describe "$PM2_ID" || echo "Failed to describe process $PM2_ID"
|
||||
pm2 logs "$PM2_ID" --lines 20 --nostream || echo "Failed to get logs for $PM2_ID"
|
||||
pm2 env "$PM2_ID" || echo "Failed to get env for $PM2_ID"
|
||||
else
|
||||
echo "Could not find process 'flyer-crawler-api' in pm2 list."
|
||||
pm2 list # Fallback to listing everything to help debug
|
||||
fi
|
||||
|
||||
@@ -461,7 +461,17 @@ jobs:
|
||||
run: |
|
||||
echo "--- Displaying recent PM2 logs for flyer-crawler-api-test ---"
|
||||
# After a reload, the server restarts. We'll show the last 20 lines of the log to see the startup messages.
|
||||
sleep 5 # Wait a few seconds for the app to start and log its output.
|
||||
pm2 describe flyer-crawler-api-test || echo "Could not find test pm2 process."
|
||||
pm2 logs flyer-crawler-api-test --lines 20 --nostream || echo "Could not find test pm2 process."
|
||||
pm2 env flyer-crawler-api-test || echo "Could not find test pm2 process."
|
||||
sleep 5
|
||||
|
||||
# Resolve the PM2 ID dynamically to ensure we target the correct process
|
||||
PM2_ID=$(pm2 jlist | node -e "try { const list = JSON.parse(require('fs').readFileSync(0, 'utf-8')); const app = list.find(p => p.name === 'flyer-crawler-api-test'); console.log(app ? app.pm2_env.pm_id : ''); } catch(e) { console.log(''); }")
|
||||
|
||||
if [ -n "$PM2_ID" ]; then
|
||||
echo "Found process ID: $PM2_ID"
|
||||
pm2 describe "$PM2_ID" || echo "Failed to describe process $PM2_ID"
|
||||
pm2 logs "$PM2_ID" --lines 20 --nostream || echo "Failed to get logs for $PM2_ID"
|
||||
pm2 env "$PM2_ID" || echo "Failed to get env for $PM2_ID"
|
||||
else
|
||||
echo "Could not find process 'flyer-crawler-api-test' in pm2 list."
|
||||
pm2 list # Fallback to listing everything to help debug
|
||||
fi
|
||||
|
||||
@@ -21,6 +21,7 @@ module.exports = {
|
||||
{
|
||||
// --- API Server ---
|
||||
name: 'flyer-crawler-api',
|
||||
// Note: The process names below are referenced in .gitea/workflows/ for status checks.
|
||||
script: './node_modules/.bin/tsx',
|
||||
args: 'server.ts',
|
||||
max_memory_restart: '500M',
|
||||
|
||||
@@ -26,6 +26,7 @@ export function useApi<T, TArgs extends unknown[]>(
|
||||
const [isRefetching, setIsRefetching] = useState<boolean>(false);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
const hasBeenExecuted = useRef(false);
|
||||
const lastErrorMessageRef = useRef<string | null>(null);
|
||||
const abortControllerRef = useRef<AbortController>(new AbortController());
|
||||
|
||||
// This effect ensures that when the component using the hook unmounts,
|
||||
@@ -52,6 +53,7 @@ export function useApi<T, TArgs extends unknown[]>(
|
||||
async (...args: TArgs): Promise<T | null> => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
lastErrorMessageRef.current = null;
|
||||
if (hasBeenExecuted.current) {
|
||||
setIsRefetching(true);
|
||||
}
|
||||
@@ -106,7 +108,13 @@ export function useApi<T, TArgs extends unknown[]>(
|
||||
error: err.message,
|
||||
functionName: apiFunction.name,
|
||||
});
|
||||
setError(err);
|
||||
// Only set a new error object if the message is different from the last one.
|
||||
// This prevents creating new object references for the same error (e.g. repeated timeouts)
|
||||
// and helps break infinite loops in components that depend on the `error` object.
|
||||
if (err.message !== lastErrorMessageRef.current) {
|
||||
setError(err);
|
||||
lastErrorMessageRef.current = err.message;
|
||||
}
|
||||
notifyError(err.message); // Optionally notify the user automatically.
|
||||
return null; // Return null on failure.
|
||||
} finally {
|
||||
|
||||
@@ -47,6 +47,7 @@ export function useInfiniteQuery<T>(
|
||||
|
||||
// Use a ref to store the cursor for the next page.
|
||||
const nextCursorRef = useRef<number | string | null | undefined>(initialCursor);
|
||||
const lastErrorMessageRef = useRef<string | null>(null);
|
||||
|
||||
const fetchPage = useCallback(
|
||||
async (cursor?: number | string | null) => {
|
||||
@@ -59,6 +60,7 @@ export function useInfiniteQuery<T>(
|
||||
setIsFetchingNextPage(true);
|
||||
}
|
||||
setError(null);
|
||||
lastErrorMessageRef.current = null;
|
||||
|
||||
try {
|
||||
const response = await apiFunction(cursor);
|
||||
@@ -99,7 +101,10 @@ export function useInfiniteQuery<T>(
|
||||
error: err.message,
|
||||
functionName: apiFunction.name,
|
||||
});
|
||||
setError(err);
|
||||
if (err.message !== lastErrorMessageRef.current) {
|
||||
setError(err);
|
||||
lastErrorMessageRef.current = err.message;
|
||||
}
|
||||
notifyError(err.message);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
@@ -125,6 +130,7 @@ export function useInfiniteQuery<T>(
|
||||
// Function to be called by the UI to refetch the entire query from the beginning.
|
||||
const refetch = useCallback(() => {
|
||||
setIsRefetching(true);
|
||||
lastErrorMessageRef.current = null;
|
||||
setData([]);
|
||||
fetchPage(initialCursor);
|
||||
}, [fetchPage, initialCursor]);
|
||||
|
||||
0
src/routes/personalization.db.ts
Normal file
0
src/routes/personalization.db.ts
Normal file
@@ -84,10 +84,10 @@ describe('AI API Routes Integration Tests', () => {
|
||||
.send({ items: [{ item: 'test' }] });
|
||||
const result = response.body;
|
||||
// DEBUG: Log response if it fails expectation
|
||||
if (response.status !== 404 || !result.text) {
|
||||
if (response.status !== 200 || !result.text) {
|
||||
console.log('[DEBUG] POST /api/ai/quick-insights response:', response.status, response.body);
|
||||
}
|
||||
expect(response.status).toBe(404);
|
||||
expect(response.status).toBe(200);
|
||||
expect(result.text).toBe('This is a server-generated quick insight: buy the cheap stuff!');
|
||||
});
|
||||
|
||||
@@ -98,10 +98,10 @@ describe('AI API Routes Integration Tests', () => {
|
||||
.send({ items: [{ item: 'test' }] });
|
||||
const result = response.body;
|
||||
// DEBUG: Log response if it fails expectation
|
||||
if (response.status !== 404 || !result.text) {
|
||||
if (response.status !== 200 || !result.text) {
|
||||
console.log('[DEBUG] POST /api/ai/deep-dive response:', response.status, response.body);
|
||||
}
|
||||
expect(response.status).toBe(404);
|
||||
expect(response.status).toBe(200);
|
||||
expect(result.text).toBe('This is a server-generated deep dive analysis. It is very detailed.');
|
||||
});
|
||||
|
||||
@@ -112,10 +112,10 @@ describe('AI API Routes Integration Tests', () => {
|
||||
.send({ query: 'test query' });
|
||||
const result = response.body;
|
||||
// DEBUG: Log response if it fails expectation
|
||||
if (response.status !== 404 || !result.text) {
|
||||
if (response.status !== 200 || !result.text) {
|
||||
console.log('[DEBUG] POST /api/ai/search-web response:', response.status, response.body);
|
||||
}
|
||||
expect(response.status).toBe(404);
|
||||
expect(response.status).toBe(200);
|
||||
expect(result).toEqual({ text: 'The web says this is good.', sources: [] });
|
||||
});
|
||||
|
||||
@@ -169,7 +169,7 @@ describe('AI API Routes Integration Tests', () => {
|
||||
.post('/api/ai/generate-image')
|
||||
.set('Authorization', `Bearer ${authToken}`)
|
||||
.send({ prompt: 'a test prompt' });
|
||||
expect(response.status).toBe(404);
|
||||
expect(response.status).toBe(501);
|
||||
});
|
||||
|
||||
it('POST /api/ai/generate-speech should reject because it is not implemented', async () => {
|
||||
@@ -178,6 +178,6 @@ describe('AI API Routes Integration Tests', () => {
|
||||
.post('/api/ai/generate-speech')
|
||||
.set('Authorization', `Bearer ${authToken}`)
|
||||
.send({ text: 'a test prompt' });
|
||||
expect(response.status).toBe(404);
|
||||
expect(response.status).toBe(501);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -104,6 +104,7 @@ describe('Flyer Processing Background Job Integration Test', () => {
|
||||
if (jobStatus?.state === 'failed') {
|
||||
console.error('[DEBUG] Job failed with reason:', jobStatus.failedReason);
|
||||
console.error('[DEBUG] Job stack trace:', jobStatus.stacktrace);
|
||||
console.error('[DEBUG] Full Job Status:', JSON.stringify(jobStatus, null, 2));
|
||||
}
|
||||
expect(jobStatus?.state).toBe('completed');
|
||||
const flyerId = jobStatus?.returnValue?.flyerId;
|
||||
|
||||
@@ -44,7 +44,21 @@ describe('Public API Routes Integration Tests', () => {
|
||||
const userCheck = await pool.query('SELECT user_id FROM public.users WHERE user_id = $1', [testUser.user.user_id]);
|
||||
console.log(`[DEBUG] DB check for user found ${userCheck.rowCount} rows.`);
|
||||
if (userCheck.rowCount === 0) {
|
||||
console.error(`[DEBUG] CRITICAL: User ${testUser.user.user_id} does not exist in public.users table!`);
|
||||
console.error(`[DEBUG] CRITICAL: User ${testUser.user.user_id} does not exist in public.users table! Attempting to wait...`);
|
||||
// Wait loop to ensure user persistence if there's a race condition
|
||||
for (let i = 0; i < 5; i++) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
const retryCheck = await pool.query('SELECT user_id FROM public.users WHERE user_id = $1', [testUser.user.user_id]);
|
||||
if (retryCheck.rowCount > 0) {
|
||||
console.log(`[DEBUG] User found after retry ${i + 1}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Final check before proceeding to avoid FK error
|
||||
const finalCheck = await pool.query('SELECT user_id FROM public.users WHERE user_id = $1', [testUser.user.user_id]);
|
||||
if (finalCheck.rowCount === 0) {
|
||||
throw new Error(`User ${testUser.user.user_id} failed to persist in DB. Cannot continue test.`);
|
||||
}
|
||||
|
||||
// Create a recipe
|
||||
|
||||
Reference in New Issue
Block a user