test: cover conversational persistence limits
This commit is contained in:
@@ -10,10 +10,12 @@ import {
|
||||
type ConversationalRectificationRpcClient,
|
||||
} from "../src/lib/conversational-rectification/store.ts";
|
||||
import { ConversationalRectificationBilling } from "../src/lib/conversational-rectification/billing.ts";
|
||||
import { conversationalRectificationTurnSchema } from "../src/lib/conversational-rectification/contracts.ts";
|
||||
import {
|
||||
conversationalRectificationActionReceiptRequestSchema,
|
||||
conversationalRectificationActionReceiptResponseSchema,
|
||||
declaredBirthInputSchema,
|
||||
lifeEventEvidenceSchema,
|
||||
privateCandidateSchema,
|
||||
validationReceiptSchema,
|
||||
} from "../src/lib/conversational-rectification/persistence-contracts.ts";
|
||||
@@ -472,6 +474,88 @@ test("declared birth input is strict, source-aware, bounded, and location-comple
|
||||
assert.equal(exactClue.birthTimeClue, " exact clue spacing ");
|
||||
});
|
||||
|
||||
test("durable JSON uses PostgreSQL-stable numeric vectors recursively", () => {
|
||||
const stableVector = {
|
||||
zero: 0,
|
||||
minFraction: 0.000001,
|
||||
decimal: 0.123456,
|
||||
maxSafe: 9_007_199_254_740_991,
|
||||
};
|
||||
assert.equal(postgresJsonbTextBytes(stableVector), 86);
|
||||
assert.equal(declaredBirthInputSchema.safeParse({
|
||||
...storedRow.declared_birth_input,
|
||||
birthplace: {
|
||||
...storedRow.declared_birth_input.birthplace,
|
||||
latitude: 25.0268,
|
||||
longitude: 121.5434,
|
||||
timezoneOffset: 8,
|
||||
},
|
||||
}).success, true);
|
||||
|
||||
for (const candidateWeights of [[1e-100], [0.1234567]]) {
|
||||
assert.equal(privateCandidateSchema.safeParse({
|
||||
resultId,
|
||||
calculationVersion: "rectification-v3.1",
|
||||
candidateWeights,
|
||||
}).success, false);
|
||||
}
|
||||
assert.equal(privateCandidateSchema.safeParse({
|
||||
resultId,
|
||||
calculationVersion: "rectification-v3.1",
|
||||
scoredHistoricalEvidence: [{
|
||||
evidenceId: "00000000-0000-4000-8000-000000000105",
|
||||
domain: "career",
|
||||
candidateTime: "05:21",
|
||||
score: 1e-100,
|
||||
ruleRefs: [],
|
||||
}],
|
||||
}).success, false);
|
||||
assert.equal(declaredBirthInputSchema.safeParse({
|
||||
...storedRow.declared_birth_input,
|
||||
birthplace: {
|
||||
...storedRow.declared_birth_input.birthplace,
|
||||
latitude: 1e-100,
|
||||
},
|
||||
}).success, false);
|
||||
assert.equal(postgresJsonbTextBytes({ nested: [{ score: 1e-100 }] }), Number.POSITIVE_INFINITY);
|
||||
});
|
||||
|
||||
test("evidence recap enforces the SQL-matched aggregate byte limit", () => {
|
||||
const evidenceRecap = Array.from({ length: 9 }, (_, index) => ({
|
||||
id: `00000000-0000-4000-8000-${(700 + index).toString().padStart(12, "0")}`,
|
||||
summary: "事".repeat(900),
|
||||
dateLabel: "2020-01",
|
||||
}));
|
||||
const turn = { ...firstTurn, evidenceRecap };
|
||||
|
||||
assert.ok(postgresJsonbTextBytes(evidenceRecap) > 24_576);
|
||||
assert.ok(postgresJsonbTextBytes(turn) < 65_536);
|
||||
assert.equal(conversationalRectificationTurnSchema.safeParse(turn).success, false);
|
||||
});
|
||||
|
||||
test("life-event evidence rejects unknown, blank, and non-boolean durable values", () => {
|
||||
const evidence = {
|
||||
id: "00000000-0000-4000-8000-000000000105",
|
||||
rawText: "2019 年 7 月换工作",
|
||||
domain: "career" as const,
|
||||
eventSummary: "换工作",
|
||||
dateValue: "2019-07",
|
||||
datePrecision: "month" as const,
|
||||
extractionStatus: "clear" as const,
|
||||
scoreable: true,
|
||||
};
|
||||
assert.equal(lifeEventEvidenceSchema.safeParse(evidence).success, true);
|
||||
for (const invalid of [
|
||||
{ ...evidence, extra: "discard me" },
|
||||
{ ...evidence, eventSummary: " \t " },
|
||||
{ ...evidence, dateValue: " \t " },
|
||||
{ ...evidence, scoreable: null },
|
||||
{ ...evidence, scoreable: "true" },
|
||||
]) {
|
||||
assert.equal(lifeEventEvidenceSchema.safeParse(invalid).success, false);
|
||||
}
|
||||
});
|
||||
|
||||
test("durable private and receipt schemas accept boundaries and reject oversize or unknown fields", () => {
|
||||
assert.equal(postgresJsonbTextBytes({ a: [1, 2] }), 13);
|
||||
assert.equal(validationReceiptSchema.safeParse({
|
||||
@@ -579,7 +663,7 @@ test("public turn JSON fields reject field and byte boundary violations", () =>
|
||||
const nearTurnLimit = {
|
||||
...exact,
|
||||
narrative: "n".repeat(12_000),
|
||||
evidenceRecap: Array.from({ length: 16 }, (_, index) => ({
|
||||
evidenceRecap: Array.from({ length: 7 }, (_, index) => ({
|
||||
id: `00000000-0000-4000-8000-${(300 + index).toString().padStart(12, "0")}`,
|
||||
summary: "事".repeat(1_000),
|
||||
dateLabel: "d".repeat(80),
|
||||
|
||||
@@ -194,8 +194,8 @@ def test_legacy_import_is_waived_without_changing_credits() -> None:
|
||||
assert "imported_from_case_id" in body
|
||||
assert "'migration_waived'" in body
|
||||
assert "insert into public.birth_time_rectification_billing" in body
|
||||
assert "credits =" not in body
|
||||
assert "credit_transactions" not in body
|
||||
assert "set credits = profile.credits -" not in body
|
||||
assert "'reserve', -" not in body
|
||||
assert "update public.birth_time_rectification_cases" not in body
|
||||
for preserved_profile_field in (
|
||||
"birth_time_clue",
|
||||
@@ -263,16 +263,21 @@ def test_start_identity_and_account_concurrency_are_server_guarded() -> None:
|
||||
|
||||
|
||||
def test_new_account_start_recovers_a_committed_pre_case_reservation() -> None:
|
||||
reserve = _function(_normalized(BILLING), "reserve_conversational_rectification_fee")
|
||||
billing = _normalized(BILLING)
|
||||
reserve = _function(billing, "reserve_conversational_rectification_fee")
|
||||
recovery = _function(
|
||||
billing, "recover_conversational_rectification_orphan_reservations"
|
||||
)
|
||||
|
||||
assert "orphan_billing.state = 'reserved'" in reserve
|
||||
assert "orphan_case.id is null" in reserve
|
||||
assert "set credits = profile.credits + v_orphan.price" in reserve
|
||||
assert "'refund', v_orphan.price" in reserve
|
||||
assert "set state = 'released'" in reserve
|
||||
assert "'recover_fee'" in reserve
|
||||
assert "v_orphan.case_id" in reserve
|
||||
assert reserve.index("set state = 'released'") < reserve.index(
|
||||
assert "orphan_billing.state = 'reserved'" in recovery
|
||||
assert "orphan_case.id is null" in recovery
|
||||
assert "set credits = profile.credits + v_orphan.price" in recovery
|
||||
assert "'refund', v_orphan.price" in recovery
|
||||
assert "set state = 'released'" in recovery
|
||||
assert "'recover_fee'" in recovery
|
||||
assert "v_orphan.case_id" in recovery
|
||||
assert "recover_conversational_rectification_orphan_reservations" in reserve
|
||||
assert reserve.index("recover_conversational_rectification_orphan_reservations") < reserve.index(
|
||||
"set credits = profile.credits - p_price"
|
||||
)
|
||||
|
||||
@@ -301,6 +306,109 @@ def test_durable_json_columns_have_byte_and_field_shape_guards() -> None:
|
||||
assert "conversational_rectification_valid_private_candidate(candidate_result)" in sql
|
||||
|
||||
|
||||
def test_durable_json_numbers_and_evidence_recap_share_postgres_bounds() -> None:
|
||||
sql = _normalized(SCHEMA)
|
||||
|
||||
assert "create or replace function public.conversational_rectification_numbers_are_stable" in sql
|
||||
for validator in (
|
||||
"conversational_rectification_valid_declared_birth_input",
|
||||
"conversational_rectification_valid_private_candidate",
|
||||
"conversational_rectification_valid_public_turn",
|
||||
"conversational_rectification_valid_action_request",
|
||||
"conversational_rectification_valid_action_response",
|
||||
):
|
||||
assert "conversational_rectification_numbers_are_stable" in _function(sql, validator)
|
||||
recap = _function(sql, "conversational_rectification_valid_evidence_recap")
|
||||
assert "octet_length(p_value::text) <= 24576" in recap
|
||||
|
||||
|
||||
def test_life_event_evidence_is_strictly_validated_before_insert() -> None:
|
||||
schema = _normalized(SCHEMA)
|
||||
transitions = _normalized(TRANSITIONS)
|
||||
validator = _function(schema, "conversational_rectification_valid_life_event_evidence")
|
||||
save = _function(transitions, "save_conversational_rectification_turn")
|
||||
|
||||
for invariant in (
|
||||
"conversational_rectification_has_only_keys",
|
||||
"jsonb_typeof(p_value -> 'id')",
|
||||
"conversational_rectification_valid_uuid_text",
|
||||
"p_value ->> 'id' !~* '^[0-9a-f]{8}-",
|
||||
"conversational_rectification_text_is_nonblank( p_value ->> 'rawtext'",
|
||||
"conversational_rectification_text_is_nonblank( p_value ->> 'eventsummary'",
|
||||
"conversational_rectification_text_is_nonblank( p_value ->> 'datevalue'",
|
||||
"jsonb_typeof(p_value -> 'scoreable')",
|
||||
):
|
||||
assert invariant in validator
|
||||
for key in (
|
||||
"id",
|
||||
"rawtext",
|
||||
"domain",
|
||||
"eventsummary",
|
||||
"datevalue",
|
||||
"dateprecision",
|
||||
"extractionstatus",
|
||||
"scoreable",
|
||||
):
|
||||
assert f"'{key}'" in validator
|
||||
evidence_array_validator = "conversational_rectification_valid_life_event_evidence_array"
|
||||
assert evidence_array_validator in save
|
||||
assert save.index(evidence_array_validator) < save.index(
|
||||
"insert into public.birth_time_rectification_event_evidence"
|
||||
)
|
||||
assert "coalesce((item ->> 'scoreable')::boolean, false)" not in save
|
||||
assert "nullif(item ->> 'datevalue', '')" not in save
|
||||
|
||||
|
||||
def test_case_mutations_enforce_cumulative_load_limits_under_the_case_lock() -> None:
|
||||
transitions = _normalized(TRANSITIONS)
|
||||
helper = _function(transitions, "conversational_rectification_case_fits_load_limits")
|
||||
|
||||
assert "count(*)" in helper
|
||||
assert "<= 2000" in helper
|
||||
assert "octet_length(v_projection::text) <= 4194304" in helper
|
||||
for name in (
|
||||
"save_conversational_rectification_turn",
|
||||
"pause_conversational_rectification_case",
|
||||
"abandon_conversational_rectification_case",
|
||||
"confirm_conversational_rectification_candidate",
|
||||
):
|
||||
body = _function(transitions, name)
|
||||
assert body.index("for update") < body.index(
|
||||
"conversational_rectification_case_fits_load_limits"
|
||||
)
|
||||
assert body.index("conversational_rectification_case_fits_load_limits") < body.index(
|
||||
"insert into public.birth_time_rectification_turns"
|
||||
)
|
||||
|
||||
|
||||
def test_legacy_import_recovers_orphan_reservations_without_a_paid_start() -> None:
|
||||
billing = _normalized(BILLING)
|
||||
transitions = _normalized(TRANSITIONS)
|
||||
recovery = _function(
|
||||
billing, "recover_conversational_rectification_orphan_reservations"
|
||||
)
|
||||
imported = _function(
|
||||
transitions, "import_legacy_conversational_rectification_case"
|
||||
)
|
||||
|
||||
for invariant in (
|
||||
"conversational-rectification-case",
|
||||
"for update",
|
||||
"orphan_billing.state = 'reserved'",
|
||||
"orphan_case.id is null",
|
||||
"set credits = profile.credits + v_orphan.price",
|
||||
"'refund', v_orphan.price",
|
||||
"set state = 'released'",
|
||||
"'recover_fee'",
|
||||
):
|
||||
assert invariant in recovery
|
||||
assert "recover_conversational_rectification_orphan_reservations" in imported
|
||||
assert imported.index("recover_conversational_rectification_orphan_reservations") < imported.index(
|
||||
"'migration_waived'"
|
||||
)
|
||||
assert "set credits = profile.credits -" not in imported
|
||||
|
||||
|
||||
def test_declared_birth_input_is_strict_source_aware_and_location_complete() -> None:
|
||||
sql = _normalized(SCHEMA)
|
||||
body = _function(sql, "conversational_rectification_valid_declared_birth_input")
|
||||
|
||||
@@ -303,6 +303,64 @@ def _create_case(
|
||||
)
|
||||
|
||||
|
||||
def _complete(database: PgDatabase, user_id: str, action_id: str, version: int = 0) -> dict[str, object]:
|
||||
result = database.sql(
|
||||
f"""
|
||||
select row_to_json(completion)::text
|
||||
from public.complete_conversational_rectification_fee(
|
||||
'{user_id}'::uuid,
|
||||
'{action_id}'::uuid,
|
||||
{version},
|
||||
'{action_id}'::uuid
|
||||
) completion;
|
||||
"""
|
||||
)
|
||||
return json.loads(result)
|
||||
|
||||
|
||||
def _save_statement(
|
||||
user_id: str,
|
||||
case_id: str,
|
||||
expected_version: int,
|
||||
action_id: str,
|
||||
evidence: list[dict[str, object]],
|
||||
*,
|
||||
turn: dict[str, object] | None = None,
|
||||
validation_receipt: dict[str, object] | None = None,
|
||||
private_candidate: dict[str, object] | None = None,
|
||||
) -> str:
|
||||
next_turn = turn or {
|
||||
**_valid_turn(case_id),
|
||||
"turnVersion": expected_version + 1,
|
||||
}
|
||||
return f"""
|
||||
select public.save_conversational_rectification_turn(
|
||||
'{user_id}'::uuid,
|
||||
'{case_id}'::uuid,
|
||||
{expected_version},
|
||||
'{action_id}'::uuid,
|
||||
{_jsonb(next_turn)},
|
||||
{_jsonb(evidence)},
|
||||
{_jsonb(validation_receipt or {"modelId": "synthetic-model", "schemaValidated": True})},
|
||||
{_jsonb(private_candidate or _valid_private_candidate())}
|
||||
)::text;
|
||||
"""
|
||||
|
||||
|
||||
def _create_legacy_case(database: PgDatabase, user_id: str, legacy_case_id: str) -> None:
|
||||
database.sql(
|
||||
f"""
|
||||
insert into public.birth_time_rectification_cases (
|
||||
id, user_id, status, reported_date, reported_time, source,
|
||||
uncertainty_before_minutes, uncertainty_after_minutes, journey_protocol
|
||||
) values (
|
||||
'{legacy_case_id}'::uuid, '{user_id}'::uuid, 'rectifying',
|
||||
'1990-01-01', '05:20', 'legacy_import', 0, 0, 'legacy-guided-v1'
|
||||
);
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def test_committed_pre_case_reservation_is_recovered_by_a_fresh_account_action(
|
||||
pg14_database: PgDatabase,
|
||||
) -> None:
|
||||
@@ -572,3 +630,332 @@ def test_database_rejects_oversize_or_unknown_durable_json(pg14_database: PgData
|
||||
where user_id = '{user_id}'::uuid and action_kind = 'reserve_fee';
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def test_postgres_uses_the_shared_stable_numeric_boundary_vectors(
|
||||
pg14_database: PgDatabase,
|
||||
) -> None:
|
||||
stable_vector = {
|
||||
"zero": 0,
|
||||
"minFraction": 0.000001,
|
||||
"decimal": 0.123456,
|
||||
"maxSafe": 9_007_199_254_740_991,
|
||||
}
|
||||
numeric_result = pg14_database.sql(
|
||||
f"""
|
||||
select pg_catalog.jsonb_build_object(
|
||||
'valid', public.conversational_rectification_numbers_are_stable({_jsonb(stable_vector)}),
|
||||
'bytes', pg_catalog.octet_length(({_jsonb(stable_vector)})::text)
|
||||
)::text;
|
||||
"""
|
||||
)
|
||||
assert json.loads(numeric_result) == {"valid": True, "bytes": 86}
|
||||
|
||||
for invalid in (
|
||||
{"nested": [{"score": 1e-100}]},
|
||||
{"nested": [{"score": 0.1234567}]},
|
||||
{"nested": [{"score": 9_007_199_254_740_992}]},
|
||||
):
|
||||
assert pg14_database.sql(
|
||||
"select public.conversational_rectification_numbers_are_stable("
|
||||
f"{_jsonb(invalid)})::text"
|
||||
) == "false"
|
||||
|
||||
assert pg14_database.sql(
|
||||
"select public.conversational_rectification_valid_private_candidate("
|
||||
f"{_jsonb({**_valid_private_candidate(), 'candidateWeights': [1e-100]})})::text"
|
||||
) == "false"
|
||||
unstable_declared_input = {
|
||||
**_valid_declared_birth_input(),
|
||||
"birthplace": {
|
||||
**_valid_declared_birth_input()["birthplace"],
|
||||
"latitude": 1e-100,
|
||||
},
|
||||
}
|
||||
assert pg14_database.sql(
|
||||
"select public.conversational_rectification_valid_declared_birth_input("
|
||||
f"{_jsonb(unstable_declared_input)})::text"
|
||||
) == "false"
|
||||
|
||||
|
||||
def test_save_rejects_invalid_evidence_without_discarding_or_coercing_fields(
|
||||
pg14_database: PgDatabase,
|
||||
) -> None:
|
||||
user_id = "00000000-0000-4000-8000-000000000961"
|
||||
case_id = "00000000-0000-4000-8000-000000000962"
|
||||
_create_user(pg14_database, user_id)
|
||||
_reserve(pg14_database, user_id, case_id)
|
||||
_create_case(pg14_database, user_id, case_id, _valid_declared_birth_input())
|
||||
_complete(pg14_database, user_id, case_id)
|
||||
|
||||
evidence = {
|
||||
"id": "00000000-0000-4000-8000-000000000963",
|
||||
"rawText": "2019 年 7 月换工作",
|
||||
"domain": "career",
|
||||
"eventSummary": "换工作",
|
||||
"dateValue": "2019-07",
|
||||
"datePrecision": "month",
|
||||
"extractionStatus": "clear",
|
||||
"scoreable": True,
|
||||
}
|
||||
invalid_values = (
|
||||
{**evidence, "unknown": "must not be discarded"},
|
||||
{**evidence, "id": "not-a-uuid"},
|
||||
{**evidence, "id": "00000000000040008000000000000001"},
|
||||
{**evidence, "rawText": 42},
|
||||
{**evidence, "domain": "finance"},
|
||||
{**evidence, "eventSummary": " \t "},
|
||||
{**evidence, "eventSummary": "\u00a0\u2007\ufeff"},
|
||||
{**evidence, "dateValue": " \t "},
|
||||
{**evidence, "datePrecision": "quarter"},
|
||||
{**evidence, "extractionStatus": "guessed"},
|
||||
{**evidence, "scoreable": None},
|
||||
{**evidence, "scoreable": "true"},
|
||||
)
|
||||
for index, invalid in enumerate(invalid_values):
|
||||
action_id = f"00000000-0000-4000-8000-{970 + index:012d}"
|
||||
assert pg14_database.rejects(
|
||||
_save_statement(user_id, case_id, 0, action_id, [invalid])
|
||||
)
|
||||
|
||||
assert pg14_database.sql(
|
||||
f"select count(*) from public.birth_time_rectification_event_evidence where case_id = '{case_id}'::uuid"
|
||||
) == "0"
|
||||
valid_without_optional_scoreable = {key: value for key, value in evidence.items() if key != "scoreable"}
|
||||
pg14_database.sql(
|
||||
_save_statement(
|
||||
user_id,
|
||||
case_id,
|
||||
0,
|
||||
"00000000-0000-4000-8000-000000000999",
|
||||
[valid_without_optional_scoreable],
|
||||
)
|
||||
)
|
||||
assert pg14_database.sql(
|
||||
f"select scoreable::text from public.birth_time_rectification_event_evidence where case_id = '{case_id}'::uuid"
|
||||
) == "false"
|
||||
|
||||
|
||||
def test_save_rejects_cumulative_evidence_count_before_inserting(
|
||||
pg14_database: PgDatabase,
|
||||
) -> None:
|
||||
user_id = "00000000-0000-4000-8000-000000000981"
|
||||
case_id = "00000000-0000-4000-8000-000000000982"
|
||||
_create_user(pg14_database, user_id)
|
||||
_reserve(pg14_database, user_id, case_id)
|
||||
_create_case(pg14_database, user_id, case_id, _valid_declared_birth_input())
|
||||
_complete(pg14_database, user_id, case_id)
|
||||
pg14_database.sql(
|
||||
f"""
|
||||
insert into public.birth_time_rectification_event_evidence (
|
||||
id, case_id, source_turn_id, raw_text, domain, event_summary,
|
||||
date_value, date_precision, extraction_status, scoreable
|
||||
)
|
||||
select pg_catalog.md5('evidence-count-' || series)::uuid,
|
||||
'{case_id}'::uuid, turn.id, 'event', 'career', 'summary',
|
||||
null, 'unknown', 'clear', false
|
||||
from pg_catalog.generate_series(1, 2000) series
|
||||
cross join public.birth_time_rectification_turns turn
|
||||
where turn.case_id = '{case_id}'::uuid and turn.turn_version = 0;
|
||||
"""
|
||||
)
|
||||
extra = {
|
||||
"id": "00000000-0000-4000-8000-000000000983",
|
||||
"rawText": "one more",
|
||||
"domain": "career",
|
||||
"eventSummary": "one more",
|
||||
"dateValue": None,
|
||||
"datePrecision": "unknown",
|
||||
"extractionStatus": "clear",
|
||||
"scoreable": False,
|
||||
}
|
||||
|
||||
assert pg14_database.rejects(
|
||||
_save_statement(
|
||||
user_id,
|
||||
case_id,
|
||||
0,
|
||||
"00000000-0000-4000-8000-000000000984",
|
||||
[extra],
|
||||
)
|
||||
)
|
||||
assert pg14_database.sql(
|
||||
f"select count(*) from public.birth_time_rectification_event_evidence where case_id = '{case_id}'::uuid"
|
||||
) == "2000"
|
||||
assert pg14_database.sql(
|
||||
f"select count(*) from public.birth_time_rectification_turns where case_id = '{case_id}'::uuid"
|
||||
) == "1"
|
||||
|
||||
|
||||
def test_save_rejects_cumulative_validation_receipt_count_before_inserting(
|
||||
pg14_database: PgDatabase,
|
||||
) -> None:
|
||||
user_id = "00000000-0000-4000-8000-000000000985"
|
||||
case_id = "00000000-0000-4000-8000-000000000986"
|
||||
_create_user(pg14_database, user_id)
|
||||
_reserve(pg14_database, user_id, case_id)
|
||||
_create_case(pg14_database, user_id, case_id, _valid_declared_birth_input())
|
||||
_complete(pg14_database, user_id, case_id)
|
||||
base_turn = _valid_turn(case_id)
|
||||
last_turn = {**base_turn, "turnVersion": 1999}
|
||||
pg14_database.sql(
|
||||
f"""
|
||||
insert into public.birth_time_rectification_turns (
|
||||
case_id, turn_version, narrative, candidate, technical_receipt,
|
||||
evidence_request, evidence_recap, actions, output_validation_receipt
|
||||
)
|
||||
select '{case_id}'::uuid, series, 'seeded turn',
|
||||
{_jsonb(base_turn['candidate'])}, {_jsonb(base_turn['technicalReceipt'])},
|
||||
{_jsonb(base_turn['evidenceRequest'])}, '[]'::jsonb,
|
||||
{_jsonb(base_turn['actions'])},
|
||||
{_jsonb({'modelId': 'synthetic-model', 'schemaValidated': True})}
|
||||
from pg_catalog.generate_series(1, 1999) series;
|
||||
update public.birth_time_rectification_cases
|
||||
set turn_version = 1999,
|
||||
turn_state = {_jsonb(last_turn)},
|
||||
journey_snapshot = {_jsonb(last_turn)}
|
||||
where id = '{case_id}'::uuid;
|
||||
"""
|
||||
)
|
||||
|
||||
assert pg14_database.rejects(
|
||||
_save_statement(
|
||||
user_id,
|
||||
case_id,
|
||||
1999,
|
||||
"00000000-0000-4000-8000-000000000987",
|
||||
[],
|
||||
)
|
||||
)
|
||||
assert pg14_database.sql(
|
||||
f"select count(*) from public.birth_time_rectification_turns where case_id = '{case_id}'::uuid"
|
||||
) == "2000"
|
||||
|
||||
|
||||
def test_save_rejects_a_projected_load_envelope_over_four_mib(
|
||||
pg14_database: PgDatabase,
|
||||
) -> None:
|
||||
user_id = "00000000-0000-4000-8000-000000000988"
|
||||
case_id = "00000000-0000-4000-8000-000000000989"
|
||||
_create_user(pg14_database, user_id)
|
||||
_reserve(pg14_database, user_id, case_id)
|
||||
_create_case(pg14_database, user_id, case_id, _valid_declared_birth_input())
|
||||
_complete(pg14_database, user_id, case_id)
|
||||
pg14_database.sql(
|
||||
f"""
|
||||
insert into public.birth_time_rectification_event_evidence (
|
||||
id, case_id, source_turn_id, raw_text, domain, event_summary,
|
||||
date_value, date_precision, extraction_status, scoreable
|
||||
)
|
||||
select pg_catalog.md5('evidence-bytes-' || series)::uuid,
|
||||
'{case_id}'::uuid, turn.id, pg_catalog.repeat('事', 4000),
|
||||
'career', pg_catalog.repeat('事', 1000), null, 'unknown', 'clear', false
|
||||
from pg_catalog.generate_series(1, 275) series
|
||||
cross join public.birth_time_rectification_turns turn
|
||||
where turn.case_id = '{case_id}'::uuid and turn.turn_version = 0;
|
||||
"""
|
||||
)
|
||||
before_bytes = int(pg14_database.sql(
|
||||
f"select pg_catalog.octet_length(public.load_conversational_rectification_case('{user_id}'::uuid, '{case_id}'::uuid)::text)"
|
||||
))
|
||||
assert 4_194_304 - 16_384 < before_bytes <= 4_194_304
|
||||
extra = {
|
||||
"id": "00000000-0000-4000-8000-000000000990",
|
||||
"rawText": "事" * 4_000,
|
||||
"domain": "career",
|
||||
"eventSummary": "事" * 1_000,
|
||||
"dateValue": "d" * 80,
|
||||
"datePrecision": "range",
|
||||
"extractionStatus": "corrected",
|
||||
"scoreable": True,
|
||||
}
|
||||
|
||||
assert pg14_database.rejects(
|
||||
_save_statement(
|
||||
user_id,
|
||||
case_id,
|
||||
0,
|
||||
"00000000-0000-4000-8000-000000000991",
|
||||
[extra],
|
||||
)
|
||||
)
|
||||
assert pg14_database.sql(
|
||||
f"select count(*) from public.birth_time_rectification_event_evidence where case_id = '{case_id}'::uuid"
|
||||
) == "275"
|
||||
|
||||
|
||||
def test_crash_then_legacy_import_refunds_orphan_without_an_unrelated_paid_start(
|
||||
pg14_database: PgDatabase,
|
||||
) -> None:
|
||||
user_id = "00000000-0000-4000-8000-000000000992"
|
||||
lost_action = "00000000-0000-4000-8000-000000000993"
|
||||
legacy_case_id = "00000000-0000-4000-8000-000000000994"
|
||||
import_action = "00000000-0000-4000-8000-000000000995"
|
||||
_create_user(pg14_database, user_id, credits=10)
|
||||
_create_legacy_case(pg14_database, user_id, legacy_case_id)
|
||||
assert _reserve(pg14_database, user_id, lost_action)["credits"] == 7
|
||||
|
||||
statement = f"""
|
||||
select public.import_legacy_conversational_rectification_case(
|
||||
'{user_id}'::uuid, '{import_action}'::uuid, '{legacy_case_id}'::uuid,
|
||||
0, '{import_action}'::uuid, 3, null,
|
||||
{_jsonb(_valid_turn(import_action))},
|
||||
{_jsonb({'modelId': 'synthetic-model', 'schemaValidated': True})},
|
||||
{_jsonb(_valid_private_candidate())}
|
||||
)::text;
|
||||
"""
|
||||
imported = json.loads(pg14_database.sql(statement))
|
||||
assert imported["billing_state"] == "migration_waived"
|
||||
assert json.loads(pg14_database.sql(
|
||||
f"""
|
||||
select pg_catalog.jsonb_build_object(
|
||||
'credits', profile.credits,
|
||||
'orphanState', orphan.state,
|
||||
'importState', imported.state,
|
||||
'importBalance', imported.balance_after,
|
||||
'reserves', pg_catalog.count(*) filter (where tx.transaction_type = 'reserve'),
|
||||
'refunds', pg_catalog.count(*) filter (where tx.transaction_type = 'refund'),
|
||||
'recoveryReceipts', (
|
||||
select pg_catalog.count(*)
|
||||
from public.birth_time_rectification_action_receipts receipt
|
||||
where receipt.user_id = profile.id and receipt.action_kind = 'recover_fee'
|
||||
)
|
||||
)::text
|
||||
from public.profiles profile
|
||||
join public.birth_time_rectification_billing orphan on orphan.case_id = '{lost_action}'::uuid
|
||||
join public.birth_time_rectification_billing imported on imported.case_id = '{import_action}'::uuid
|
||||
left join public.credit_transactions tx on tx.user_id = profile.id
|
||||
where profile.id = '{user_id}'::uuid
|
||||
group by profile.id, profile.credits, orphan.state, imported.state, imported.balance_after;
|
||||
"""
|
||||
)) == {
|
||||
"credits": 10,
|
||||
"orphanState": "released",
|
||||
"importState": "migration_waived",
|
||||
"importBalance": 10,
|
||||
"reserves": 1,
|
||||
"refunds": 1,
|
||||
"recoveryReceipts": 1,
|
||||
}
|
||||
assert json.loads(pg14_database.sql(
|
||||
f"""
|
||||
select pg_catalog.jsonb_build_object(
|
||||
'kind', receipt.request ->> 'kind',
|
||||
'credits', (receipt.response ->> 'credits')::integer,
|
||||
'transactionRequest', tx.request_id
|
||||
)::text
|
||||
from public.birth_time_rectification_action_receipts receipt
|
||||
join public.credit_transactions tx
|
||||
on tx.user_id = receipt.user_id and tx.transaction_type = 'refund'
|
||||
where receipt.user_id = '{user_id}'::uuid and receipt.action_kind = 'recover_fee';
|
||||
"""
|
||||
)) == {
|
||||
"kind": "recover_fee",
|
||||
"credits": 10,
|
||||
"transactionRequest": f"rectification:{lost_action}",
|
||||
}
|
||||
|
||||
assert json.loads(pg14_database.sql(statement)) == imported
|
||||
assert pg14_database.sql(
|
||||
f"select count(*) from public.credit_transactions where user_id = '{user_id}'::uuid"
|
||||
) == "2"
|
||||
|
||||
Reference in New Issue
Block a user