175 lines
5.7 KiB
TypeScript
175 lines
5.7 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { readFileSync } from "node:fs";
|
|
import test from "node:test";
|
|
|
|
const financeMigration = readFileSync(
|
|
new URL(
|
|
"../supabase/migrations/20260721150000_align_conversational_finance_domain.sql",
|
|
import.meta.url,
|
|
),
|
|
"utf8",
|
|
);
|
|
const healthPressureMigration = readFileSync(
|
|
new URL(
|
|
"../supabase/migrations/20260722170000_align_conversational_health_pressure_domain.sql",
|
|
import.meta.url,
|
|
),
|
|
"utf8",
|
|
);
|
|
const requestCardinalityMigration = readFileSync(
|
|
new URL(
|
|
"../supabase/migrations/20260722180000_align_conversational_evidence_request_cardinality.sql",
|
|
import.meta.url,
|
|
),
|
|
"utf8",
|
|
);
|
|
const followUpMigration = readFileSync(
|
|
new URL(
|
|
"../supabase/migrations/20260723030000_align_conversational_follow_up_request.sql",
|
|
import.meta.url,
|
|
),
|
|
"utf8",
|
|
);
|
|
const assistantOnlyRegenerateMigration = readFileSync(
|
|
new URL(
|
|
"../supabase/migrations/20260724030000_allow_assistant_only_rectification_regenerate.sql",
|
|
import.meta.url,
|
|
),
|
|
"utf8",
|
|
);
|
|
const structuredDateConfirmationMigration = readFileSync(
|
|
new URL(
|
|
"../supabase/migrations/20260725010000_structured_conversational_date_confirmation.sql",
|
|
import.meta.url,
|
|
),
|
|
"utf8",
|
|
);
|
|
const repairedStructuredDateValidatorMigration = readFileSync(
|
|
new URL(
|
|
"../supabase/migrations/20260725020000_repair_structured_conversational_date_validator.sql",
|
|
import.meta.url,
|
|
),
|
|
"utf8",
|
|
);
|
|
const productionMigrationWorkflow = readFileSync(
|
|
new URL(
|
|
"../../.github/workflows/apply-production-rectification-migrations.yml",
|
|
import.meta.url,
|
|
),
|
|
"utf8",
|
|
);
|
|
|
|
test("durable rectification SQL accepts every application evidence domain", () => {
|
|
for (const validator of [
|
|
"conversational_rectification_valid_evidence_request(jsonb)",
|
|
"conversational_rectification_valid_life_event_evidence(jsonb)",
|
|
"conversational_rectification_valid_private_candidate(jsonb)",
|
|
]) {
|
|
assert.match(financeMigration, new RegExp(validator.replace(/[()]/g, "\\$&")));
|
|
}
|
|
assert.match(financeMigration, /'education', 'finance', 'relocation'/);
|
|
assert.match(financeMigration, /birth_time_rectification_event_evidence_domain_check/);
|
|
});
|
|
|
|
test("durable public recap accepts and validates its optional domain", () => {
|
|
assert.match(
|
|
financeMigration,
|
|
/array\['id', 'summary', 'dateLabel', 'domain', 'isCorrection'\]/,
|
|
);
|
|
assert.match(financeMigration, /item \? 'domain'/);
|
|
assert.match(financeMigration, /item ->> 'domain' not in/);
|
|
});
|
|
|
|
test("durable public turns accept health pressure follow-up requests", () => {
|
|
for (const validator of [
|
|
"conversational_rectification_valid_evidence_request(jsonb)",
|
|
"conversational_rectification_valid_evidence_recap(jsonb)",
|
|
"conversational_rectification_valid_life_event_evidence(jsonb)",
|
|
"conversational_rectification_valid_private_candidate(jsonb)",
|
|
]) {
|
|
assert.match(
|
|
healthPressureMigration,
|
|
new RegExp(validator.replace(/[()]/g, "\\$&")),
|
|
);
|
|
}
|
|
assert.match(healthPressureMigration, /'finance'', ''health_pressure'', ''relocation'/);
|
|
assert.match(
|
|
healthPressureMigration,
|
|
/'finance', 'health_pressure', 'relocation'/,
|
|
);
|
|
assert.match(healthPressureMigration, /birth_time_rectification_event_evidence_domain_check/);
|
|
});
|
|
|
|
test("durable evidence requests allow one focused follow-up domain", () => {
|
|
assert.match(
|
|
requestCardinalityMigration,
|
|
/conversational_rectification_valid_evidence_request\(jsonb\)/,
|
|
);
|
|
assert.match(requestCardinalityMigration, /'between 2 and 4'/);
|
|
assert.match(requestCardinalityMigration, /'between 1 and 4'/);
|
|
});
|
|
|
|
test("durable evidence requests accept bounded follow-up targeting", () => {
|
|
assert.match(
|
|
followUpMigration,
|
|
/array\['domains', 'datePrecision', 'freeTextAllowed', 'followUp'\]/,
|
|
);
|
|
assert.match(followUpMigration, /array\['kind', 'evidenceId'\]/);
|
|
assert.match(
|
|
followUpMigration,
|
|
/'new_event', 'event_date', 'event_detail'/,
|
|
);
|
|
assert.match(followUpMigration, /valid_uuid_text/);
|
|
});
|
|
|
|
test("durable regenerate turns may persist an assistant-only replacement", () => {
|
|
assert.match(assistantOnlyRegenerateMigration, /p_user_message is not null and/);
|
|
assert.match(assistantOnlyRegenerateMigration, /v_saved_user_message is distinct from p_user_message/);
|
|
assert.doesNotMatch(
|
|
assistantOnlyRegenerateMigration,
|
|
/if p_user_message is null\s+or/,
|
|
);
|
|
});
|
|
|
|
|
|
test("durable evidence requests persist strict structured date confirmation", () => {
|
|
assert.match(
|
|
structuredDateConfirmationMigration,
|
|
/array\['domains', 'datePrecision', 'freeTextAllowed', 'prompt', 'followUp'\]/,
|
|
);
|
|
assert.match(
|
|
structuredDateConfirmationMigration,
|
|
/array\['kind', 'evidenceId', 'answerMode', 'proposedDate'\]/,
|
|
);
|
|
assert.match(structuredDateConfirmationMigration, /'free_text', 'yes_no'/);
|
|
assert.match(structuredDateConfirmationMigration, /yes\/no date proposals/i);
|
|
assert.match(structuredDateConfirmationMigration, /valid_uuid_text/);
|
|
});
|
|
|
|
test("repaired structured date validator relies on precision-specific date checks", () => {
|
|
assert.doesNotMatch(
|
|
repairedStructuredDateValidatorMigration,
|
|
/\^\[0-9\]\{4\}\(-/,
|
|
);
|
|
assert.match(
|
|
repairedStructuredDateValidatorMigration,
|
|
/proposedDate,precision}' = 'month'/,
|
|
);
|
|
assert.match(
|
|
repairedStructuredDateValidatorMigration,
|
|
/proposedDate,precision}' = 'day'/,
|
|
);
|
|
});
|
|
|
|
test("production workflow uploads and applies the structured date confirmation migrations", () => {
|
|
for (const migration of [
|
|
"20260725010000_structured_conversational_date_confirmation",
|
|
"20260725020000_repair_structured_conversational_date_validator",
|
|
]) {
|
|
assert.equal(
|
|
productionMigrationWorkflow.match(new RegExp(`${migration}\\.sql`, "g"))?.length,
|
|
2,
|
|
);
|
|
}
|
|
});
|