evo360-types 1.3.503 → 1.3.507

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,5 @@
1
1
  export * from "./fb_collections";
2
+ export * from "./test-suite";
2
3
  import type { IFireDoc } from "../shared";
3
4
  export declare const EvoIntegrationsPermissions: {
4
5
  readonly List: "evo_integrations_read";
@@ -18,6 +18,7 @@ exports.OBLIGATORY_CAPABILITIES = exports.SYNC_RUN_HANDLER = exports.SYNC_RUN_PU
18
18
  exports.isInternalCapability = isInternalCapability;
19
19
  exports.splitCapabilityName = splitCapabilityName;
20
20
  __exportStar(require("./fb_collections"), exports);
21
+ __exportStar(require("./test-suite"), exports);
21
22
  // ======================================================
22
23
  // evo-integrations
23
24
  //
@@ -1,4 +1,5 @@
1
1
  export * from "./fb_collections";
2
+ export * from "./test-suite";
2
3
  import type { IFireDoc } from "../shared";
3
4
 
4
5
  // ======================================================
@@ -0,0 +1,211 @@
1
+ import type { IFireDoc } from "../shared";
2
+ export declare const TEST_RUNS_COLLECTION = "test-runs";
3
+ export declare const TEST_RUN_STEPS_COLLECTION = "steps";
4
+ /**
5
+ * O agrupamento é derivado da DEPENDÊNCIA REAL DE ESTADO, não de estética:
6
+ * o `external_id` devolvido pelo create alimenta get/update/confirm/revert/cancel,
7
+ * e o do patient.create alimenta o round-trip e o delete.
8
+ */
9
+ export type TestSuiteId = "reading" | "appointment" | "sync" | "patient" | "cleanup";
10
+ export interface ITestSuiteMeta {
11
+ id: TestSuiteId;
12
+ label: string;
13
+ /** Ordem estrita = o passo N depende do estado produzido pelo N-1. */
14
+ ordered: boolean;
15
+ description: string;
16
+ }
17
+ export declare const TEST_SUITES: readonly ITestSuiteMeta[];
18
+ export type TestStepId = "caps.list" | "ui.config" | "procedure.list" | "status.list" | "dicts.extra" | "patient.list" | "patient.get" | "appointment.list" | "appointment.create" | "appointment.get" | "appointment.update" | "appointment.confirm" | "appointment.revert_confirm" | "appointment.cancel" | "sync.force" | "patient.create" | "patient.roundtrip" | "patient.delete" | "residue.sweep";
19
+ export interface ITestStepMeta {
20
+ id: TestStepId;
21
+ suite: TestSuiteId;
22
+ label: string;
23
+ /**
24
+ * `true` = o passo ESCREVE no PMS de produção.
25
+ *
26
+ * Usado em dois lugares: para o modo somente-leitura e para classificar o custo
27
+ * de REPETIR um passo — repetir leitura é livre, repetir escrita duplica efeito.
28
+ */
29
+ writes: boolean;
30
+ /** Passos que precisam ter passado antes. Vazio = pode rodar sozinho. */
31
+ dependsOn: readonly TestStepId[];
32
+ /** Capability invocada, quando houver. Nem todo passo tem uma. */
33
+ capability?: string;
34
+ }
35
+ /**
36
+ * Ordem do array = ordem de execução. Não é 1:1 com capability:
37
+ * · `residue.sweep` não invoca capability nenhuma (varre a agenda externa);
38
+ * · `sync.force` chama `integrations_sync_monitor`, não uma capability;
39
+ * · `appointment.list` aparece em dois passos (leitura e varredura);
40
+ * · `patient.roundtrip` combina `patient.get` + `patient.update`.
41
+ */
42
+ export declare const TEST_STEPS: readonly ITestStepMeta[];
43
+ /**
44
+ * Suíte padrão (D10) — a LINHA DE BASE COMPARÁVEL: todos os passos.
45
+ *
46
+ * Só uma corrida com exatamente esta seleção é comparável com a anterior. Sem isso
47
+ * o output principal da ferramenta ("nenhum defeito novo") seria infalsificável:
48
+ * bastaria desmarcar o passo que falha.
49
+ *
50
+ * Atenção: a seleção não basta. `comparable` também exige `target.create_patient`
51
+ * ligado — senão `patient.create`/`patient.delete` são pulados e a cobertura real
52
+ * fica menor que a nominal. Quem decide isso é o backend, em `computeComparable`.
53
+ */
54
+ export declare const STANDARD_SUITE_STEP_IDS: readonly TestStepId[];
55
+ export declare const READ_ONLY_STEP_IDS: readonly TestStepId[];
56
+ export type TestCheckStatus = "pass" | "fail" | "skipped";
57
+ export interface ITestCheck {
58
+ /**
59
+ * Id ESTÁVEL no formato `<step_id>:<slug>` — ex. `appointment.update:sem_divergencia`.
60
+ *
61
+ * É a chave que o catálogo de conhecidos referencia. O script antigo casava por
62
+ * REGEX no texto do label, o que quebrava nos dois sentidos: mudar a redação
63
+ * descatalogava um defeito em silêncio, e um regex largo engolia defeito novo de
64
+ * outra causa. Por isso o id vem antes do texto — o label pode ser reescrito à
65
+ * vontade.
66
+ */
67
+ id: string;
68
+ label: string;
69
+ status: TestCheckStatus;
70
+ detail?: string;
71
+ }
72
+ export type TestStepStatus = "pending" | "running" | "pass" | "known" | "fail" | "skipped" | "error";
73
+ export interface ITestSchemaValidation {
74
+ ok: boolean;
75
+ errors?: Array<{
76
+ path: (string | number)[];
77
+ message: string;
78
+ code?: string;
79
+ }>;
80
+ }
81
+ /**
82
+ * O wire — o que o BACKEND mandou para o n8n, lido de
83
+ * `integrations_sync_monitor GET /calls?run_id=`.
84
+ *
85
+ * É a camada que interessa a quem mantém os workflows: a resposta da action mostra
86
+ * o que a UI mandou, não o que saiu na linha. `DocumentReference` vivo no payload
87
+ * (a classe de bug que derrubou produção no revert_confirm) só aparece aqui.
88
+ */
89
+ export interface ITestStepWire {
90
+ request_json?: string;
91
+ response_json?: string;
92
+ n8n_execution_id?: string | null;
93
+ n8n_workflow_id?: string | null;
94
+ workflow_url?: string;
95
+ http_status?: number | null;
96
+ timed_out?: boolean | null;
97
+ error_message?: string | null;
98
+ request_truncated?: boolean | null;
99
+ response_truncated?: boolean | null;
100
+ }
101
+ export interface ITestRunStep extends IFireDoc {
102
+ step_id: TestStepId;
103
+ suite: TestSuiteId;
104
+ order: number;
105
+ status: TestStepStatus;
106
+ started_at?: unknown;
107
+ finished_at?: unknown;
108
+ duration_ms?: number;
109
+ /** Camada 1 — o que a UI mandou para `integrations_actions` e o que voltou. */
110
+ layer1?: {
111
+ request?: unknown;
112
+ response?: unknown;
113
+ };
114
+ /** Correlação com o wire: `transport_metadata.run_id` da resposta da action. */
115
+ n8n_run_id?: string | null;
116
+ /** Camada 2 — o que o BE mandou para o n8n. */
117
+ wire?: ITestStepWire | null;
118
+ schema_validation?: {
119
+ request?: ITestSchemaValidation;
120
+ response?: ITestSchemaValidation;
121
+ } | null;
122
+ checks: ITestCheck[];
123
+ /** Preenchido quando `status === 'known'`: qual defeito catalogado casou. */
124
+ known_defects?: Array<{
125
+ check_id: string;
126
+ description: string;
127
+ ref: string;
128
+ }>;
129
+ error?: string | null;
130
+ }
131
+ export type TestRunStatus = "running" | "finished" | "abandoned";
132
+ /** Dados do paciente novo, quando o usuário liga a criação (D3). */
133
+ export interface ITestNewPatient {
134
+ first_name: string;
135
+ last_name?: string;
136
+ mobile_phone?: string;
137
+ email?: string;
138
+ social_id?: string;
139
+ birth_date?: string;
140
+ }
141
+ export interface ITestRunTarget {
142
+ /** Paciente de teste JÁ EXISTENTE, usado na leitura e no agendamento. */
143
+ patient_id: string;
144
+ /** Só quando `create_patient` está ligado. */
145
+ new_patient?: ITestNewPatient | null;
146
+ create_patient: boolean;
147
+ /** `YYYY-MM-DD`. Recomendado no passado — é aviso, não trava (D2). */
148
+ slot_date: string;
149
+ /** `HH:MM`. */
150
+ slot_time: string;
151
+ /** Dia povoado para o `appointment.list`; sem isso usa `slot_date`. */
152
+ list_date?: string | null;
153
+ }
154
+ /**
155
+ * Diagnóstico de segurança — AVISO, não trava (D2).
156
+ *
157
+ * Os dois avisos têm gravidade diferente e a UI precisa refletir isso: data futura
158
+ * só ativa as varreduras D-2/D-0, que dependem de janela; gatilho de evento ligado
159
+ * dispara na hora do create.
160
+ */
161
+ export interface ITestRunPreflight {
162
+ slot_in_past: boolean;
163
+ /** Quais dos gatilhos de evento estão ligados na agenda-alvo. */
164
+ triggers_enabled: string[];
165
+ /**
166
+ * `notification_config.configs` vazio.
167
+ *
168
+ * Distinguir de "desligado" importa: nada dispara nos dois casos, mas aqui é por
169
+ * AUSÊNCIA DE CONFIGURAÇÃO, não por decisão de alguém.
170
+ */
171
+ notification_config_empty: boolean;
172
+ evaluated_at?: unknown;
173
+ }
174
+ /** O que a corrida criou no PMS — permite enumerar resíduo quando o `Z` é desmarcado (D11). */
175
+ export interface ITestRunInventory {
176
+ appointment_external_ids: string[];
177
+ patient_external_id?: string | null;
178
+ }
179
+ export interface ITestRunVerdict {
180
+ ok: number;
181
+ known: number;
182
+ new_defects: number;
183
+ }
184
+ export interface ITestRun extends IFireDoc {
185
+ adapter_id: string;
186
+ tenant: string;
187
+ calendar_id: string;
188
+ target: ITestRunTarget;
189
+ selection: {
190
+ preset: "standard" | "read-only" | "custom";
191
+ step_ids: TestStepId[];
192
+ };
193
+ /** Quantos passos da suíte padrão esta corrida cobriu. */
194
+ coverage: {
195
+ ran: number;
196
+ total: number;
197
+ };
198
+ /** `true` só quando a seleção é exatamente a suíte padrão (D10). */
199
+ comparable: boolean;
200
+ preflight: ITestRunPreflight;
201
+ inventory: ITestRunInventory;
202
+ verdict: ITestRunVerdict;
203
+ status: TestRunStatus;
204
+ /**
205
+ * Dono da corrida. Quem orquestra é o FE, então duas abas conduzindo a mesma
206
+ * corrida escreveriam na mesma agenda de produção em paralelo — outro usuário
207
+ * abre em modo leitura até assumir explicitamente.
208
+ */
209
+ created_by: string;
210
+ finished_at?: unknown;
211
+ }
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ // ======================================================
3
+ // evo-integrations — bateria de regressão de adapters (feat-123)
4
+ //
5
+ // Porte do script `_regressao-adapters.cjs` para ferramenta de produto no Nexus.
6
+ //
7
+ // A ferramenta é APARTADA: roda por fora, chamando as APIs (`integrations_actions`,
8
+ // `integrations_admin`, `integrations_sync_monitor`) exatamente como o script faz.
9
+ // Quem orquestra a sequência é o FE; quem persiste o resultado é o BE.
10
+ //
11
+ // Este módulo é o contrato COMPARTILHADO entre os dois lados:
12
+ // · o FE monta a seleção a partir do catálogo de passos;
13
+ // · o BE valida a seleção contra o MESMO catálogo e calcula o veredito.
14
+ //
15
+ // O catálogo de defeitos CONHECIDOS não mora aqui — mora no backend do Nexus,
16
+ // porque só ele calcula veredito (o FE apenas renderiza o que recebe).
17
+ // ======================================================
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.READ_ONLY_STEP_IDS = exports.STANDARD_SUITE_STEP_IDS = exports.TEST_STEPS = exports.TEST_SUITES = exports.TEST_RUN_STEPS_COLLECTION = exports.TEST_RUNS_COLLECTION = void 0;
20
+ // Path: /platform/evo-integrations/test-runs/{run_id}
21
+ exports.TEST_RUNS_COLLECTION = "test-runs";
22
+ // Path: /platform/evo-integrations/test-runs/{run_id}/steps/{step_id}
23
+ exports.TEST_RUN_STEPS_COLLECTION = "steps";
24
+ exports.TEST_SUITES = [
25
+ {
26
+ id: "reading",
27
+ label: "Dicionários e leitura",
28
+ ordered: false,
29
+ description: "Capabilities de consulta. Nenhuma escreve — pode rodar sozinha com efeito colateral zero.",
30
+ },
31
+ {
32
+ id: "appointment",
33
+ label: "Ciclo de vida do agendamento",
34
+ ordered: true,
35
+ description: "create → get → update → confirm → revert_confirm → cancel. Cada passo depende do external_id do create.",
36
+ },
37
+ {
38
+ id: "sync",
39
+ label: "Sync não ressuscita",
40
+ ordered: true,
41
+ description: "Força o sync de 1 dia e confere que o agendamento cancelado NÃO volta. Exige o cancel da suíte anterior.",
42
+ },
43
+ {
44
+ id: "patient",
45
+ label: "Paciente",
46
+ ordered: true,
47
+ description: "create → round-trip (get+update) → delete. O create nunca foi exercitado por nenhuma rodada do script.",
48
+ },
49
+ {
50
+ id: "cleanup",
51
+ label: "Varredura de resíduo",
52
+ ordered: false,
53
+ description: "Lê a agenda EXTERNA e cancela o que tiver o marcador. Selecionável — quem roda é o dev de integrações, que responde pelo que escolhe.",
54
+ },
55
+ ];
56
+ /**
57
+ * Ordem do array = ordem de execução. Não é 1:1 com capability:
58
+ * · `residue.sweep` não invoca capability nenhuma (varre a agenda externa);
59
+ * · `sync.force` chama `integrations_sync_monitor`, não uma capability;
60
+ * · `appointment.list` aparece em dois passos (leitura e varredura);
61
+ * · `patient.roundtrip` combina `patient.get` + `patient.update`.
62
+ */
63
+ exports.TEST_STEPS = [
64
+ // ── S1 — leitura ────────────────────────────────────────────────────────────
65
+ { id: "caps.list", suite: "reading", label: "capabilities", writes: false, dependsOn: [] },
66
+ { id: "ui.config", suite: "reading", label: "ui-config", writes: false, dependsOn: [] },
67
+ { id: "procedure.list", suite: "reading", label: "procedure.list", writes: false, dependsOn: [], capability: "appointment.procedure.list" },
68
+ { id: "status.list", suite: "reading", label: "status.list", writes: false, dependsOn: [], capability: "appointment.status.list" },
69
+ { id: "dicts.extra", suite: "reading", label: "dicionários extra", writes: false, dependsOn: [] },
70
+ { id: "patient.list", suite: "reading", label: "patient.list", writes: false, dependsOn: [], capability: "patient.list" },
71
+ { id: "patient.get", suite: "reading", label: "patient.get", writes: false, dependsOn: [], capability: "patient.get" },
72
+ { id: "appointment.list", suite: "reading", label: "appointment.list", writes: false, dependsOn: [], capability: "appointment.list" },
73
+ // ── S2 — ciclo de vida ──────────────────────────────────────────────────────
74
+ { id: "appointment.create", suite: "appointment", label: "create", writes: true, dependsOn: [], capability: "appointment.create" },
75
+ { id: "appointment.get", suite: "appointment", label: "get", writes: false, dependsOn: ["appointment.create"], capability: "appointment.get" },
76
+ { id: "appointment.update", suite: "appointment", label: "update", writes: true, dependsOn: ["appointment.create"], capability: "appointment.update" },
77
+ { id: "appointment.confirm", suite: "appointment", label: "confirm", writes: true, dependsOn: ["appointment.create"], capability: "appointment.confirm" },
78
+ { id: "appointment.revert_confirm", suite: "appointment", label: "revert_confirm", writes: true, dependsOn: ["appointment.confirm"], capability: "appointment.revert_confirm" },
79
+ { id: "appointment.cancel", suite: "appointment", label: "cancel", writes: true, dependsOn: ["appointment.create"], capability: "appointment.cancel" },
80
+ // ── S3 — sync ───────────────────────────────────────────────────────────────
81
+ { id: "sync.force", suite: "sync", label: "sync (1 dia)", writes: true, dependsOn: ["appointment.cancel"] },
82
+ // ── S4 — paciente ───────────────────────────────────────────────────────────
83
+ { id: "patient.create", suite: "patient", label: "patient.create", writes: true, dependsOn: [], capability: "patient.create" },
84
+ { id: "patient.roundtrip", suite: "patient", label: "round-trip (get+update)", writes: true, dependsOn: [], capability: "patient.update" },
85
+ { id: "patient.delete", suite: "patient", label: "patient.delete", writes: true, dependsOn: ["patient.create"], capability: "patient.delete" },
86
+ // ── Z — limpeza ─────────────────────────────────────────────────────────────
87
+ { id: "residue.sweep", suite: "cleanup", label: "varredura de resíduo", writes: true, dependsOn: [] },
88
+ ];
89
+ /**
90
+ * Suíte padrão (D10) — a LINHA DE BASE COMPARÁVEL: todos os passos.
91
+ *
92
+ * Só uma corrida com exatamente esta seleção é comparável com a anterior. Sem isso
93
+ * o output principal da ferramenta ("nenhum defeito novo") seria infalsificável:
94
+ * bastaria desmarcar o passo que falha.
95
+ *
96
+ * Atenção: a seleção não basta. `comparable` também exige `target.create_patient`
97
+ * ligado — senão `patient.create`/`patient.delete` são pulados e a cobertura real
98
+ * fica menor que a nominal. Quem decide isso é o backend, em `computeComparable`.
99
+ */
100
+ exports.STANDARD_SUITE_STEP_IDS = exports.TEST_STEPS.map((s) => s.id);
101
+ exports.READ_ONLY_STEP_IDS = exports.TEST_STEPS.filter((s) => !s.writes).map((s) => s.id);