test(17-02): D-06 ownerTenantId tagging for RSS ingestion + seed idempotency
- RssAdapter.fetchTenders tags every record from a feed with a tenantId with the same D-13 ownerTenantId origin marking email-alert records carry since Phase 14; platform-wide feeds (no tenantId) stay unmarked. The pure parseFeed mapping is untouched — tagging happens in the fan-out loop that knows which row a batch came from - Extracted the service.bund.de seed out of TendersModule.onModuleInit into seedServiceBundRssFeed() (tenders.seed.ts, same pattern as the existing seedTendersModule), so the find-then-create idempotency added in Task 1 is unit-tested directly instead of only via a Nest bootstrap - New rss-feed-migration-sql.spec.ts: text-only check of the Task 1 migration file (nullable columns, dropped/created indexes, no existing-row mutation, correct ordering) - Files modified: apps/api/src/tenders/adapters/rss.adapter.ts, apps/api/src/tenders/tenders.module.ts, apps/api/src/tenders/tenders.seed.ts, apps/api/src/tenders/adapters/rss.adapter.spec.ts, apps/api/src/tenders/tenders.seed.spec.ts, apps/api/src/tenders/rss-feed-migration-sql.spec.ts
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { ModuleRegistryService } from '../module-registry/module-registry.service';
|
||||
import type { PrismaService } from '../prisma/prisma.service';
|
||||
|
||||
/**
|
||||
* Seeds the tender-radar module into the module registry.
|
||||
@@ -24,3 +25,45 @@ export async function seedTendersModule(
|
||||
isSystem: true,
|
||||
});
|
||||
}
|
||||
|
||||
/** The one genuinely national/global RSS source (RESEARCH.md Open Question 3). */
|
||||
export const SERVICE_BUND_RSS_URL =
|
||||
'https://www.service.bund.de/Content/Globals/Functions/RSSFeed/RSSGenerator_Ausschreibungen.xml';
|
||||
|
||||
/**
|
||||
* Seeds the single default-active service.bund.de RSS feed row — the one
|
||||
* genuinely national/global RSS source (RESEARCH.md Open Question 3,
|
||||
* Phase 14, Plan 02, D-14). subreport-elvis has no single canonical URL
|
||||
* (per-municipality instances, RESEARCH.md Pitfall 2) — deliberately ZERO
|
||||
* subreport-elvis rows are ever seeded; users add the municipality feeds
|
||||
* relevant to them via the RSS-Feeds UI (Plan 14-02 Task 3).
|
||||
*
|
||||
* Extracted out of `TendersModule.onModuleInit` (Phase 17, Plan 02, Task 3)
|
||||
* — same "logic lives in a plain testable function, the module's lifecycle
|
||||
* hook only wraps it in try/catch + logging" pattern as `seedTendersModule`
|
||||
* above — so the idempotency behavior below can be exercised directly by a
|
||||
* unit test instead of only through a full Nest bootstrap.
|
||||
*
|
||||
* Phase 17, Plan 02 (D-02): "upsert on url" stopped working when the
|
||||
* unique index moved to `(userId, url)` — Prisma's generated
|
||||
* compound-unique input type requires `userId` as a plain `string` (not
|
||||
* `string | null | undefined`), so a platform-wide row (`userId = null`)
|
||||
* can never be addressed through it. This function instead FINDS a
|
||||
* platform-wide row with this url first, and only creates one if none
|
||||
* exists — an ordinary equality condition has no such requirement. Still
|
||||
* idempotent across repeated app starts (proven in tenders.seed.spec.ts).
|
||||
*/
|
||||
export async function seedServiceBundRssFeed(prisma: PrismaService): Promise<void> {
|
||||
const existing = await prisma.tenderRssFeedSource.findFirst({
|
||||
where: { userId: null, url: SERVICE_BUND_RSS_URL },
|
||||
});
|
||||
if (existing) return;
|
||||
|
||||
await prisma.tenderRssFeedSource.create({
|
||||
data: {
|
||||
url: SERVICE_BUND_RSS_URL,
|
||||
label: 'service-bund',
|
||||
isActive: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user