Этап 4 / Волна 18 закрытие блокеров по времени, доменной полярности и допуску доказательной базы
This commit is contained in:
@@ -28,6 +28,11 @@ describe("assistant mode API", () => {
|
||||
expect(typeof response.body.debug?.answer_grounding_check?.status).toBe("string");
|
||||
expect(Array.isArray(response.body.debug?.retrieval_status)).toBe(true);
|
||||
expect(Array.isArray(response.body.debug?.retrieval_results)).toBe(true);
|
||||
expect(typeof response.body.debug?.temporal_guard_applied).toBe("boolean");
|
||||
expect(typeof response.body.debug?.temporal_guard_outcome).toBe("string");
|
||||
expect(response.body.debug?.domain_polarity_guard).toBeTruthy();
|
||||
expect(response.body.debug?.evidence_admissibility_gate).toBeTruthy();
|
||||
expect(response.body.debug?.grounded_answer_eligibility_guard).toBeTruthy();
|
||||
expect(Array.isArray(response.body.conversation)).toBe(true);
|
||||
expect(response.body.conversation.length).toBe(2);
|
||||
});
|
||||
@@ -133,7 +138,7 @@ describe("assistant mode API", () => {
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.reply_type).not.toBe("route_mismatch_blocked");
|
||||
expect(response.body.debug?.answer_grounding_check?.status).not.toBe("route_mismatch_blocked");
|
||||
expect(["partial", "grounded"]).toContain(String(response.body.debug?.answer_grounding_check?.status));
|
||||
expect(["partial", "grounded", "no_grounded_answer"]).toContain(String(response.body.debug?.answer_grounding_check?.status));
|
||||
expect(response.body.reply_type).toBe("partial_coverage");
|
||||
});
|
||||
|
||||
@@ -150,7 +155,7 @@ describe("assistant mode API", () => {
|
||||
expect(["partial_coverage", "clarification_required", "route_mismatch_blocked", "factual_with_explanation"]).toContain(
|
||||
String(response.body.reply_type)
|
||||
);
|
||||
expect(["partial", "grounded", "route_mismatch_blocked"]).toContain(
|
||||
expect(["partial", "grounded", "route_mismatch_blocked", "no_grounded_answer"]).toContain(
|
||||
String(response.body.debug?.answer_grounding_check?.status)
|
||||
);
|
||||
expect(typeof response.body.debug?.answer_grounding_check?.route_subject_match).toBe("boolean");
|
||||
|
||||
@@ -194,6 +194,46 @@ describe.sequential("assistant follow-up state binding", () => {
|
||||
expect(second.body.debug?.investigation_state_snapshot?.turn_index).toBe(2);
|
||||
});
|
||||
|
||||
it("isolates scope for independent cross-domain turn and does not carry stale period/domain", async () => {
|
||||
const app = await createAppWithFlags({
|
||||
state: "1",
|
||||
binding: "1",
|
||||
problemUnits: "1",
|
||||
continuity: "1",
|
||||
answerPolicy: "1",
|
||||
problemCentric: "1"
|
||||
});
|
||||
const sessionId = `asst-wave17-scope-isolation-${Date.now()}`;
|
||||
|
||||
const first = await request(app).post("/api/assistant/message").send({
|
||||
session_id: sessionId,
|
||||
useMock: true,
|
||||
promptVersion: "normalizer_v2_0_2",
|
||||
user_message: "Проверь хвосты по счету 60.01 за 2020-06 и почему не закрылся долг."
|
||||
});
|
||||
expect(first.status).toBe(200);
|
||||
expect(first.body.debug?.investigation_state_snapshot?.question_scope_id).toContain("d:settlements_60_62");
|
||||
expect(first.body.debug?.investigation_state_snapshot?.scope_origin).toBe("explicit_from_message");
|
||||
|
||||
const second = await request(app).post("/api/assistant/message").send({
|
||||
session_id: sessionId,
|
||||
useMock: true,
|
||||
promptVersion: "normalizer_v2_0_2",
|
||||
user_message: "Проверь НДС по счету-фактуре: где разрыв в цепочке документ -> регистр -> книга?"
|
||||
});
|
||||
|
||||
expect(second.status).toBe(200);
|
||||
expect(second.body.debug?.followup_state_usage).toBeUndefined();
|
||||
expect(second.body.debug?.investigation_state_snapshot?.scope_origin).toBe("explicit_from_message");
|
||||
expect(String(second.body.debug?.investigation_state_snapshot?.question_scope_id ?? "")).toContain(
|
||||
"d:vat_document_register_book"
|
||||
);
|
||||
expect(second.body.debug?.investigation_state_snapshot?.focus?.period).not.toBe("2020-06");
|
||||
expect(String(second.body.debug?.investigation_state_snapshot?.focus?.domain ?? "")).not.toContain("settlements_60_62");
|
||||
expect(second.body.debug?.investigation_state_snapshot?.followup_context?.question_scope_id).toBeTruthy();
|
||||
expect(second.body.debug?.investigation_state_snapshot?.followup_context?.scope_origin).toBe("explicit_from_message");
|
||||
});
|
||||
|
||||
it("rebinds follow-up domain away from settlements on fixed-asset amortization query", async () => {
|
||||
const app = await createAppWithFlags({
|
||||
state: "1",
|
||||
|
||||
@@ -0,0 +1,359 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { resolveCompanyAnchors } from "../src/services/companyAnchorResolver";
|
||||
import {
|
||||
applyDomainPolarityGuardToRetrievalResults,
|
||||
applyEligibilityToGroundingCheck,
|
||||
applyEvidenceAdmissibilityGate,
|
||||
applyPolarityHintToExecutionPlan,
|
||||
applyTemporalHintToExecutionPlan,
|
||||
evaluateGroundedAnswerEligibility,
|
||||
resolveDomainPolarityGuard,
|
||||
resolveTemporalGuard
|
||||
} from "../src/services/assistantRuntimeGuards";
|
||||
|
||||
function buildProblemUnit(input: {
|
||||
id: string;
|
||||
lifecycleDomain: string;
|
||||
defect?: string;
|
||||
account?: string;
|
||||
}): any {
|
||||
return {
|
||||
schema_version: "problem_unit_v0_1",
|
||||
problem_unit_id: input.id,
|
||||
problem_unit_type: "unresolved_settlement_cluster",
|
||||
title: "test unit",
|
||||
mechanism_summary: input.defect ?? "mechanism",
|
||||
business_defect_class: input.defect ?? "mechanism",
|
||||
severity: { score: 0.8, grade: "high" },
|
||||
confidence: { score: 0.7, grade: "medium" },
|
||||
lifecycle_domain: input.lifecycleDomain,
|
||||
affected_entities: ["Document:DOC-1"],
|
||||
affected_documents: ["Document:DOC-1"],
|
||||
affected_postings: ["Posting:POST-1"],
|
||||
affected_accounts: [input.account ?? "60"],
|
||||
affected_counterparties: ["Counterparty:CP-1"],
|
||||
affected_contracts: ["Contract:CTR-1"],
|
||||
failed_expected_edge: "payment_to_settlement",
|
||||
period_impact: { is_period_sensitive: true, impact_class: "close_risk" },
|
||||
evidence_pack: ["cand-1"],
|
||||
entity_backlinks: [{ entity: "Document", id: "DOC-1" }],
|
||||
snapshot_limitations: []
|
||||
};
|
||||
}
|
||||
|
||||
function buildEvidence(input: {
|
||||
id: string;
|
||||
period: string;
|
||||
accountDebit?: string;
|
||||
accountCredit?: string;
|
||||
limitationCode?: string | null;
|
||||
sourceLayer?: string | null;
|
||||
}): any {
|
||||
return {
|
||||
evidence_id: input.id,
|
||||
claim_ref: "requirement:R1",
|
||||
source_type: "retrieval_item",
|
||||
source_ref: {
|
||||
schema_version: "evidence_source_ref_v1",
|
||||
namespace: "unknown",
|
||||
entity: "MCPLiveMovement",
|
||||
id: input.id,
|
||||
period: input.period,
|
||||
canonical_ref: `evidence_source_ref_v1|unknown|mcplivemovement|${String(input.id).toLowerCase()}|${input.period}`
|
||||
},
|
||||
pointer: {
|
||||
fragment_id: "F1",
|
||||
route: "hybrid_store_plus_live",
|
||||
source: {
|
||||
namespace: "unknown",
|
||||
entity: "MCPLiveMovement",
|
||||
id: input.id,
|
||||
period: input.period
|
||||
},
|
||||
locator: {
|
||||
field_path: "amount",
|
||||
item_index: 0
|
||||
}
|
||||
},
|
||||
evidence_kind: "mechanism_link",
|
||||
mechanism_note: "payment_to_settlement",
|
||||
confidence: "medium",
|
||||
limitation: input.limitationCode
|
||||
? {
|
||||
reason_code: input.limitationCode,
|
||||
note: null
|
||||
}
|
||||
: null,
|
||||
payload: {
|
||||
period: input.period,
|
||||
account_debit: input.accountDebit ?? null,
|
||||
account_credit: input.accountCredit ?? null,
|
||||
source_layer: input.sourceLayer ?? null
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function buildRetrieval(input?: Partial<any>): any {
|
||||
return {
|
||||
fragment_id: "F1",
|
||||
requirement_ids: ["R1"],
|
||||
route: "hybrid_store_plus_live",
|
||||
status: "ok",
|
||||
result_type: "chain",
|
||||
items: [],
|
||||
summary: {
|
||||
semantic_profile: {
|
||||
account_scope: ["60"],
|
||||
domain_scope: ["settlements", "customers"],
|
||||
relation_patterns: ["payment_to_settlement"]
|
||||
},
|
||||
live_mcp: {
|
||||
matched_rows: 0,
|
||||
account_scope: ["60"]
|
||||
}
|
||||
},
|
||||
evidence: [],
|
||||
candidate_evidence: [],
|
||||
problem_units: [],
|
||||
problem_unit_summary: null,
|
||||
why_included: ["test"],
|
||||
selection_reason: ["test"],
|
||||
risk_factors: ["test"],
|
||||
business_interpretation: ["test"],
|
||||
confidence: "medium",
|
||||
limitations: [],
|
||||
errors: [],
|
||||
...input
|
||||
};
|
||||
}
|
||||
|
||||
describe("stage4 blocker-pack runtime guards", () => {
|
||||
it("flags temporal anchor drift outside July 2020 snapshot", () => {
|
||||
const userMessage = "Почему по оплате от 6 июля 2020 долг по поставщику остался?";
|
||||
const temporal = resolveTemporalGuard({
|
||||
userMessage,
|
||||
companyAnchors: resolveCompanyAnchors(userMessage),
|
||||
normalized: {
|
||||
schema_version: "normalized_query_v2_0_2",
|
||||
fragments: [
|
||||
{
|
||||
fragment_id: "F1",
|
||||
time_scope: {
|
||||
type: "explicit",
|
||||
value: "2023-07-06",
|
||||
confidence: "high"
|
||||
}
|
||||
}
|
||||
]
|
||||
} as any
|
||||
});
|
||||
expect(temporal.temporal_guard_applied).toBe(true);
|
||||
expect(temporal.temporal_guard_outcome).toBe("failed_out_of_snapshot_window");
|
||||
expect(temporal.resolved_time_anchor).toBe("2020-07-06");
|
||||
expect(temporal.reason_codes).toContain("normalized_anchor_out_of_snapshot_window");
|
||||
});
|
||||
|
||||
it("locks July month window when question has month-only anchor", () => {
|
||||
const userMessage = "В июльском срезе почему по счету 60 остался хвост?";
|
||||
const temporal = resolveTemporalGuard({
|
||||
userMessage,
|
||||
companyAnchors: resolveCompanyAnchors(userMessage),
|
||||
normalized: {
|
||||
schema_version: "normalized_query_v2_0_2",
|
||||
fragments: []
|
||||
} as any
|
||||
});
|
||||
const hintedPlan = applyTemporalHintToExecutionPlan(
|
||||
[
|
||||
{
|
||||
should_execute: true,
|
||||
fragment_text: "проверить зависший долг по поставщику"
|
||||
}
|
||||
],
|
||||
temporal
|
||||
);
|
||||
expect(temporal.temporal_guard_applied).toBe(true);
|
||||
expect(temporal.temporal_guard_outcome).toBe("passed");
|
||||
expect(temporal.resolved_time_anchor).toBe("2020-07");
|
||||
expect(hintedPlan[0].fragment_text).toMatch(/июля 2020|2020-07-01/);
|
||||
});
|
||||
|
||||
it("filters customer settlement semantics from supplier/payable case", () => {
|
||||
const guard = resolveDomainPolarityGuard({
|
||||
userMessage: "По поставщику и счету 60 долг не закрылся после оплаты.",
|
||||
focusDomainHint: "settlements_60_62"
|
||||
});
|
||||
const withHint = applyPolarityHintToExecutionPlan(
|
||||
[
|
||||
{
|
||||
should_execute: true,
|
||||
fragment_text: "проверить цепочку закрытия долга"
|
||||
}
|
||||
],
|
||||
guard
|
||||
);
|
||||
const result = applyDomainPolarityGuardToRetrievalResults({
|
||||
guard,
|
||||
retrievalResults: [
|
||||
buildRetrieval({
|
||||
problem_units: [
|
||||
buildProblemUnit({
|
||||
id: "pu-supplier",
|
||||
lifecycleDomain: "bank_settlement",
|
||||
defect: "payment_to_settlement",
|
||||
account: "60"
|
||||
}),
|
||||
buildProblemUnit({
|
||||
id: "pu-customer",
|
||||
lifecycleDomain: "customer_settlement",
|
||||
defect: "stale_receivable",
|
||||
account: "62"
|
||||
})
|
||||
],
|
||||
evidence: [
|
||||
buildEvidence({
|
||||
id: "ev-supplier",
|
||||
period: "2020-07",
|
||||
accountDebit: "60.01"
|
||||
}),
|
||||
buildEvidence({
|
||||
id: "ev-customer",
|
||||
period: "2020-07",
|
||||
accountDebit: "62.01"
|
||||
})
|
||||
]
|
||||
})
|
||||
]
|
||||
});
|
||||
expect(guard.polarity).toBe("supplier_payable");
|
||||
expect(withHint[0].fragment_text).toMatch(/счет 60|поставщиком/i);
|
||||
expect(result.audit.outcome).toBe("passed");
|
||||
expect(result.audit.rejected_problem_units).toBeGreaterThan(0);
|
||||
expect(result.audit.rejected_evidence).toBeGreaterThan(0);
|
||||
const units = result.retrievalResults[0].problem_units ?? [];
|
||||
expect(units.some((item: any) => item.lifecycle_domain === "customer_settlement")).toBe(false);
|
||||
});
|
||||
|
||||
it("rejects inadmissible live evidence on zero matched_rows and wrong account/date", () => {
|
||||
const userMessage = "Почему по поставщику по счету 60 в июле 2020 хвост не закрыт?";
|
||||
const temporal = resolveTemporalGuard({
|
||||
userMessage,
|
||||
companyAnchors: resolveCompanyAnchors(userMessage),
|
||||
normalized: {
|
||||
schema_version: "normalized_query_v2_0_2",
|
||||
fragments: [
|
||||
{
|
||||
fragment_id: "F1",
|
||||
time_scope: {
|
||||
type: "explicit",
|
||||
value: "2020-07",
|
||||
confidence: "high"
|
||||
}
|
||||
}
|
||||
]
|
||||
} as any
|
||||
});
|
||||
const polarity = resolveDomainPolarityGuard({
|
||||
userMessage,
|
||||
focusDomainHint: "settlements_60_62"
|
||||
});
|
||||
const gated = applyEvidenceAdmissibilityGate({
|
||||
retrievalResults: [
|
||||
buildRetrieval({
|
||||
items: [
|
||||
{
|
||||
source_entity: "MCPLiveMovement",
|
||||
source_layer: "mcp_live_probe",
|
||||
account_debit: "68.02",
|
||||
account_credit: "19.04",
|
||||
period: "2026-02-01"
|
||||
}
|
||||
],
|
||||
evidence: [
|
||||
buildEvidence({
|
||||
id: "ev-live-1",
|
||||
period: "2026-02-01",
|
||||
accountDebit: "68.02",
|
||||
accountCredit: "19.04",
|
||||
limitationCode: "weak_source_mapping",
|
||||
sourceLayer: "mcp_live_probe"
|
||||
})
|
||||
]
|
||||
})
|
||||
],
|
||||
temporal,
|
||||
polarity: polarity.polarity,
|
||||
focusDomainHint: "settlements_60_62",
|
||||
userMessage,
|
||||
companyAnchors: resolveCompanyAnchors(userMessage)
|
||||
});
|
||||
expect(gated.audit.admissible_evidence_count).toBe(0);
|
||||
expect(gated.audit.rejected_evidence_count).toBe(1);
|
||||
expect(gated.audit.reject_breakdown.zero_live_match).toBeGreaterThan(0);
|
||||
expect(gated.audit.reject_breakdown.wrong_account_scope).toBeGreaterThan(0);
|
||||
expect(gated.audit.reject_breakdown.future_dated_or_out_of_window).toBeGreaterThan(0);
|
||||
expect(gated.retrievalResults[0].evidence).toHaveLength(0);
|
||||
expect(gated.retrievalResults[0].items).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("degrades grounded status when eligibility guard fails", () => {
|
||||
const eligibility = evaluateGroundedAnswerEligibility({
|
||||
temporal: {
|
||||
raw_time_anchor: "6 июля 2020",
|
||||
resolved_time_anchor: "2020-07-06",
|
||||
temporal_resolution_source: "company_snapshot_july_day_lock",
|
||||
temporal_guard_applied: true,
|
||||
temporal_guard_outcome: "failed_out_of_snapshot_window",
|
||||
primary_period_window: {
|
||||
from: "2020-07-06",
|
||||
to: "2020-07-06",
|
||||
granularity: "day"
|
||||
},
|
||||
reason_codes: ["normalized_anchor_out_of_snapshot_window"]
|
||||
},
|
||||
polarity: {
|
||||
applied: true,
|
||||
polarity: "supplier_payable",
|
||||
outcome: "passed",
|
||||
supplier_score: 3,
|
||||
customer_score: 0,
|
||||
account_scope: ["60"],
|
||||
rejected_problem_units: 0,
|
||||
rejected_evidence: 0,
|
||||
critical_contradiction: false,
|
||||
reason_codes: []
|
||||
},
|
||||
evidence: {
|
||||
candidate_evidence_total: 2,
|
||||
admissible_evidence_count: 0,
|
||||
rejected_evidence_count: 2,
|
||||
rejected_item_count: 1,
|
||||
reject_breakdown: {
|
||||
wrong_period: 1,
|
||||
wrong_domain: 0,
|
||||
wrong_account_scope: 1,
|
||||
weak_source_mapping: 0,
|
||||
zero_live_match: 0,
|
||||
future_dated_or_out_of_window: 1
|
||||
},
|
||||
category_breakdown: {
|
||||
hard_evidence: 0,
|
||||
supporting_signal: 0,
|
||||
inadmissible_noise: 2
|
||||
},
|
||||
reason_codes: ["no_admissible_evidence_for_grounded_answer"]
|
||||
}
|
||||
});
|
||||
const grounded = applyEligibilityToGroundingCheck(
|
||||
{
|
||||
status: "grounded",
|
||||
reasons: []
|
||||
},
|
||||
eligibility
|
||||
);
|
||||
expect(eligibility.eligible).toBe(false);
|
||||
expect(eligibility.reason_codes).toContain("admissible_evidence_count_zero");
|
||||
expect(grounded.status).toBe("no_grounded_answer");
|
||||
expect(grounded.reasons.join(" ")).toMatch(/Недостаточно допустимого evidence|Temporal anchor/i);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,81 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
buildConversationExportForCopy,
|
||||
sanitizeConversationExportText,
|
||||
type ConversationExportItem
|
||||
} from "../../frontend/src/utils/conversationExport";
|
||||
|
||||
function buildConversation(): ConversationExportItem[] {
|
||||
return [
|
||||
{
|
||||
message_id: "m-user-1",
|
||||
role: "user",
|
||||
text: "Почему не закрывается РБП за июль?",
|
||||
reply_type: null,
|
||||
created_at: "2026-03-28T18:00:00.000Z",
|
||||
trace_id: null,
|
||||
debug: null
|
||||
},
|
||||
{
|
||||
message_id: "m-asst-1",
|
||||
role: "assistant",
|
||||
text: [
|
||||
"Коротко: есть признаки неполного списания РБП.",
|
||||
"",
|
||||
"### debug_payload_json",
|
||||
"```json",
|
||||
"{\"route_summary\":\"hybrid_store_plus_live\",\"domain_scope\":[\"deferred_expense\"]}",
|
||||
"```"
|
||||
].join("\n"),
|
||||
reply_type: "partial_coverage",
|
||||
created_at: "2026-03-28T18:00:05.000Z",
|
||||
trace_id: "tr-1",
|
||||
debug: {
|
||||
route_summary: "hybrid_store_plus_live",
|
||||
coverage_report: { requirements_covered: 0 }
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
describe("conversation export regression", () => {
|
||||
it("default export excludes debug payload sections and keeps user-facing text", () => {
|
||||
const exportText = buildConversationExportForCopy("sess-1", buildConversation(), "default");
|
||||
|
||||
expect(exportText).toContain("export_mode: default");
|
||||
expect(exportText).toContain("Коротко: есть признаки неполного списания РБП.");
|
||||
expect(exportText).not.toContain("### debug_payload_json");
|
||||
expect(exportText).not.toContain("### technical_debug_payload_json");
|
||||
expect(exportText).not.toContain("coverage_report");
|
||||
expect(exportText).not.toContain("domain_scope");
|
||||
expect(exportText).not.toContain("route_summary");
|
||||
});
|
||||
|
||||
it("technical export includes debug payload in dedicated section only", () => {
|
||||
const exportText = buildConversationExportForCopy("sess-1", buildConversation(), "technical");
|
||||
|
||||
expect(exportText).toContain("export_mode: technical");
|
||||
expect(exportText).toContain("### technical_debug_payload_json");
|
||||
expect(exportText).toContain("\"route_summary\": \"hybrid_store_plus_live\"");
|
||||
expect(exportText).not.toContain("### debug_payload_json");
|
||||
});
|
||||
|
||||
it("sanitizeConversationExportText removes technical tails", () => {
|
||||
const source = [
|
||||
"Ответ для пользователя.",
|
||||
"domain_scope: [vat_flow]",
|
||||
"coverage_report: {...}",
|
||||
"### technical_breakdown_json",
|
||||
"```json",
|
||||
"{\"internal\":\"yes\"}",
|
||||
"```"
|
||||
].join("\n");
|
||||
|
||||
const sanitized = sanitizeConversationExportText(source);
|
||||
expect(sanitized).toContain("Ответ для пользователя.");
|
||||
expect(sanitized).not.toContain("domain_scope");
|
||||
expect(sanitized).not.toContain("coverage_report");
|
||||
expect(sanitized).not.toContain("technical_breakdown_json");
|
||||
expect(sanitized).not.toContain("internal");
|
||||
});
|
||||
});
|
||||
@@ -17,6 +17,8 @@ describe("investigation_state flow scaffolding", () => {
|
||||
|
||||
expect(first.status).toBe(200);
|
||||
expect(first.body.debug?.investigation_state_snapshot?.schema_version).toBe("investigation_state_v1");
|
||||
expect(first.body.debug?.investigation_state_snapshot?.question_scope_id).toBeTruthy();
|
||||
expect(first.body.debug?.investigation_state_snapshot?.scope_origin).toBe("explicit_from_message");
|
||||
expect(first.body.debug?.answer_structure_v11?.schema_version).toBe("answer_structure_v1_1");
|
||||
expect(first.body.debug?.followup_state_usage).toBeUndefined();
|
||||
|
||||
@@ -38,6 +40,8 @@ describe("investigation_state flow scaffolding", () => {
|
||||
expect(second.status).toBe(200);
|
||||
expect(second.body.debug?.investigation_state_snapshot?.turn_index).toBe(2);
|
||||
expect(second.body.debug?.followup_state_usage?.applied).toBe(true);
|
||||
expect(second.body.debug?.investigation_state_snapshot?.question_scope_id).toBeTruthy();
|
||||
expect(second.body.debug?.investigation_state_snapshot?.followup_context?.question_scope_id).toBeTruthy();
|
||||
|
||||
const sessionResponse = await request(app).get(`/api/assistant/session/${sessionId}`);
|
||||
expect(sessionResponse.status).toBe(200);
|
||||
|
||||
Reference in New Issue
Block a user