Mirrored from GitHub github.com/roostorg/coop

server: Add end-to-end item submission integration test and harness (#488) master

* wip: integration test harness + first test for item submission (#339) Adds a real-infra integration test harness under server/test/integ/ that boots the IoC container, starts the express app, and runs the ItemProcessingWorker inline so submissions flow end-to-end without mocks. - setupIntegrationServer.ts — harness - wait.ts — poll helpers for Scylla and ClickHouse - items-submission.integ.test.ts — first scenario (#339) - README.md — how to run, layout, conventions Server typecheck passes. Not yet validated against a running stack. CI workflow to be wired in a follow-up. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Fix integ harness so the items-submission test passes end-to-end Three corrections from running against the real docker-compose stack: - setupIntegrationServer: import dotenv/config so the IoC container sees env vars. test:integ does not preload dotenv like server:start does. - setupIntegrationServer: swallow the benign "Connection is closed" error during shutdown. BullMQ's Worker.close() closes the shared ioredis connection, then closeSharedResourcesForShutdown errors when it tries to quit() it again. - wait: switch the ClickHouse query from {name:Type} substitutions to ? placeholders + positional binds, matching coop's formatClickhouseQuery convention. Also drops three unnecessary optional chains in afterAll that ESLint flagged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Address PR #488 review feedback on integ harness - waitForItemInScylla now queries Scylla directly via deps.Scylla.select rather than ItemInvestigationService.getItemByIdentifier. The service falls back to the partial-items endpoint and the data warehouse when Scylla returns nothing, which would let a real Scylla write failure pass the test silently. (juanmrad) - Cleanup callbacks in items-submission.integ.test.ts are now optional and guarded with optional chaining + try/finally so a beforeAll failure surfaces the root cause instead of an "X is not a function" in afterAll. (juanmrad, coderabbit) - makeIntegrationServer().shutdown() is now best-effort: each teardown step runs through a runStep helper, errors are collected, and the combined failures are surfaced via AggregateError so one bad step doesn't leak the server or shared resources. (coderabbit) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Surface Scylla query errors instead of swallowing them waitForItemInScylla had a .catch(() => []) that turned any Scylla ResponseError into a 30s waitFor timeout — masking structural problems (bad query, schema drift) as flaky tests. cassandra-driver returns empty rows for "no match", not an exception, so the catch was never buying us "not yet" semantics anyway; it was only ever hiding bugs. Document why we don't add org_id to the WHERE clause: partition key is (org_id, synthetic_thread_id), so a partial-PK restriction would need ALLOW FILTERING. The secondary index on item_identifier is what the production lookup path uses, and the test already asserts org_id on the returned row. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>