import assert from 'node:assert/strict'; import { readFile } from 'node:fs/promises'; import test from 'node:test'; async function source(relativePath: string): Promise { return readFile(new URL(relativePath, import.meta.url), 'utf8'); } test('migration keeps the automatic feature fail-closed in storage and separate from employee replies', async () => { const sql = await source('../migrations/020_leader_task_summary_notifications.sql'); assert.match(sql, /CREATE TABLE IF NOT EXISTS leader_task_summary_subscriptions/); // The runtime reconciler is the only component that activates rows. A raw // insert must remain inert when identity or routing checks have not run. assert.match(sql, /enabled boolean NOT NULL DEFAULT false/); assert.match(sql, /scope text NOT NULL DEFAULT 'organization'/); assert.match(sql, /CHECK \(include_manual OR include_agentbus\)/); assert.match(sql, /recipient_address_ciphertext text NOT NULL/); assert.match(sql, /conversation_id_ciphertext text NOT NULL/); assert.match(sql, /payload_ciphertext text NOT NULL/); assert.match(sql, /target_verified_at timestamptz/); assert.match(sql, /UNIQUE \(organization_id, leader_user_id\)/); assert.match(sql, /UNIQUE \(subscription_id, subscription_revision, task_id, milestone\)/); assert.doesNotMatch(sql, /INSERT\s+INTO/iu, 'schema migration must not backfill or send historical tasks'); assert.doesNotMatch(sql, /REFERENCES\s+agentbus_deliveries/iu); assert.doesNotMatch(sql, /recipient_address\s+text/iu); assert.doesNotMatch(sql, /conversation_id\s+text/iu); assert.doesNotMatch(sql, /payload\s+jsonb/iu); }); test('automatic reconciliation derives enabled subscriptions from leader identity and AgentBus routing', async () => { const service = await source('../src/leader-notification-service.ts'); const channels = await source('../src/agentbus-channels.ts'); assert.match(service, /reconcileAutomaticSubscriptions/); assert.match(service, /leader\.role = 'team_lead'/); assert.match(service, /leader\.is_active = true/); assert.match(service, /leader\.erp_account IS NOT NULL/); assert.match(service, /channel\.owner_user_id = leader\.id/); assert.match(service, /latest_route\.inbound_from/); assert.match(service, /channel\.external_user_ref/); assert.match(service, /route_task\.assigned_user_id = leader\.id/); assert.match(service, /source: inboundRecipient \? 'agentbus_inbound' : 'channel_external_user_ref'/); assert.match(service, /`agentbus:\$\{recipientAddress\}`/); assert.match(service, /VALUES \(\$1, \$2, \$3, true, true, true, now\(\), 0/); assert.match(service, /target_verified_at = now\(\)/); assert.match(service, /automatic_disabled/); assert.match(service, /channel_disabled.*route_unavailable.*role_or_channel_invalid/s); assert.doesNotMatch(service, /upsertSubscription|expectedRevision|leader_summary_target_unverified/); assert.match(channels, /ownerChanged[\s\S]*?\? null[\s\S]*?: currentExternalUserRef/); }); test('projection remains organization-scoped, future-only, role-safe and encrypted', async () => { const service = await source('../src/leader-notification-service.ts'); assert.match(service, /event\.topic = 'task\.updated'/); assert.match(service, /event\.created_at >= subscription\.starts_at/); assert.match(service, /task\.assigned_user_id <> subscription\.leader_user_id/); assert.match(service, /assignee\.role <> 'admin'/); assert.match(service, /task\.source IN \('manual', 'agentbus'\)/); assert.match(service, /subscription\.include_manual/); assert.match(service, /subscription\.include_agentbus/); assert.match(service, /event\.payload ->> 'archived'.*IS DISTINCT FROM 'true'/s); assert.match(service, /event\.payload ->> 'restored'.*IS DISTINCT FROM 'true'/s); assert.match(service, /subscription\.target_verified_at IS NOT NULL/); assert.match(service, /pg_try_advisory_xact_lock/); assert.match(service, /encryptText\(this\.config, payloadText\)/); assert.match(service, /FOR UPDATE OF delivery SKIP LOCKED/); assert.match(service, /sha256Text\(payloadText\).*payload_fingerprint/s); assert.match(service, /sha256Text\(recipientAddress\).*recipient_address_fingerprint/s); assert.match(service, /sha256Text\(conversationId\).*conversation_id_fingerprint/s); assert.match(service, /delivery_status = 'cancelled'.*旧路由待发送摘要已取消/s); assert.doesNotMatch(service, /reply_to/); }); test('HTTP exposes administrator status only and no manual mutation or live test-send endpoint', async () => { const server = await source('../src/server.ts'); const routeStart = server.indexOf("app.get('/api/settings/leader-summary-subscriptions'"); const routeEnd = server.indexOf("app.post('/api/auth/logout'", routeStart); const routes = server.slice(routeStart, routeEnd); assert.ok(routeStart > 0 && routeEnd > routeStart); assert.match(routes, /requireAdminSession/); assert.doesNotMatch(routes, /app\.(put|post|patch|delete)/); assert.doesNotMatch(server, /leaderSummarySubscriptionSchema|upsertSubscription/); assert.doesNotMatch(server, /leader-summary-subscriptions.*test-send|leader-summary-subscriptions.*test\/send/s); }); test('AgentBus sends summaries as reserved low-priority proactive events without plaintext frame logging', async () => { const agentbus = await source('../src/agentbus.ts'); const ignoreStart = agentbus.indexOf('function inboundFrameIgnoreReason'); const ignoreEnd = agentbus.indexOf('export function parseAgentBusFrame', ignoreStart); assert.match(agentbus.slice(ignoreStart, ignoreEnd), /'task\.summary'/); assert.match(agentbus.slice(ignoreStart, ignoreEnd), /reserved_event/); assert.match(agentbus, /id: `leader-summary-\$\{delivery\.id\}`/); assert.match(agentbus, /to: delivery\.recipient_address/); assert.match(agentbus, /conversation_id: delivery\.conversation_id/); const frameStart = agentbus.indexOf('export function createLeaderTaskSummaryFrame'); const frameEnd = agentbus.indexOf('export function taskResultStatus', frameStart); assert.doesNotMatch(agentbus.slice(frameStart, frameEnd), /reply_to/); const flushStart = agentbus.indexOf('private async flushOutboundDeliveries'); const sendEnd = agentbus.indexOf('private async sendDurableDelivery', flushStart); const leaderDelivery = agentbus.slice(flushStart, sendEnd); assert.match(leaderDelivery, /await this\.flushDurableDeliveries\(\)/); assert.match(leaderDelivery, /await this\.flushLeaderDeliveries\(\)/); assert.match(leaderDelivery, /recipient_fingerprint/); assert.match(leaderDelivery, /conversation_fingerprint/); assert.doesNotMatch(leaderDelivery, /this\.logFrame/); assert.match(agentbus, /ownerRole === 'team_lead'/); assert.match(agentbus, /observeLeaderRoute/); assert.match(agentbus, /recipientAddress: text\(frame\.from\),\s*conversationId/s); }); test('administrator UI is read-only and explains role-driven automatic delivery', async () => { const html = await source('../../LianSyn-platform/index.html'); const app = await source('../../LianSyn-platform/app.js'); assert.match(html, /组长任务摘要抄送/); assert.match(html, /无需单独配置或启用/); assert.match(html, /只处理自动启用后的新结果,不补发历史任务/); assert.match(html, /已经到达微信的消息无法撤回/); assert.doesNotMatch(html, /leaderSummary(Form|ChannelId|RecipientAddress|ConversationId|TargetVerified|Enabled|Save)/); assert.match(app, /自动推送/); assert.match(app, /等待路由/); assert.match(app, /无需维护收件地址或微信会话 ID/); assert.doesNotMatch(app, /leader-summary-subscriptions\/\$\{|expected_revision: subscription\.revision/); assert.doesNotMatch(app, /leader-summary-subscriptions[^'"\n]*backfill/); });