@jantstack/adonis-authz 2.0.0-alpha.1 → 2.4.0-alpha.1
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.
- package/README.md +462 -35
- package/build/commands/authz_catalog_diff.js +1 -1
- package/build/commands/authz_catalog_diff.js.map +1 -1
- package/build/commands/authz_catalog_prune_orphans.d.ts +78 -0
- package/build/commands/authz_catalog_prune_orphans.d.ts.map +1 -0
- package/build/commands/authz_catalog_prune_orphans.js +136 -0
- package/build/commands/authz_catalog_prune_orphans.js.map +1 -0
- package/build/commands/authz_catalog_sync.d.ts +17 -0
- package/build/commands/authz_catalog_sync.d.ts.map +1 -1
- package/build/commands/authz_catalog_sync.js +27 -4
- package/build/commands/authz_catalog_sync.js.map +1 -1
- package/build/commands/authz_freeze.d.ts +44 -0
- package/build/commands/authz_freeze.d.ts.map +1 -0
- package/build/commands/authz_freeze.js +95 -0
- package/build/commands/authz_freeze.js.map +1 -0
- package/build/commands/authz_reconcile.d.ts +102 -0
- package/build/commands/authz_reconcile.d.ts.map +1 -0
- package/build/commands/authz_reconcile.js +294 -0
- package/build/commands/authz_reconcile.js.map +1 -0
- package/build/commands/authz_relations_reconcile.d.ts +73 -0
- package/build/commands/authz_relations_reconcile.d.ts.map +1 -0
- package/build/commands/authz_relations_reconcile.js +225 -0
- package/build/commands/authz_relations_reconcile.js.map +1 -0
- package/build/commands/authz_scopes_relay.d.ts +47 -0
- package/build/commands/authz_scopes_relay.d.ts.map +1 -0
- package/build/commands/authz_scopes_relay.js +141 -0
- package/build/commands/authz_scopes_relay.js.map +1 -0
- package/build/commands/authz_unfreeze.d.ts +37 -0
- package/build/commands/authz_unfreeze.d.ts.map +1 -0
- package/build/commands/authz_unfreeze.js +92 -0
- package/build/commands/authz_unfreeze.js.map +1 -0
- package/build/commands/main.d.ts +6 -1
- package/build/commands/main.d.ts.map +1 -1
- package/build/commands/main.js +6 -1
- package/build/commands/main.js.map +1 -1
- package/build/commands/openfga_provision.d.ts +46 -4
- package/build/commands/openfga_provision.d.ts.map +1 -1
- package/build/commands/openfga_provision.js +90 -7
- package/build/commands/openfga_provision.js.map +1 -1
- package/build/configure.d.ts +11 -0
- package/build/configure.d.ts.map +1 -1
- package/build/configure.js +37 -1
- package/build/configure.js.map +1 -1
- package/build/index.d.ts +39 -10
- package/build/index.d.ts.map +1 -1
- package/build/index.js +35 -6
- package/build/index.js.map +1 -1
- package/build/providers/authz_provider.d.ts +26 -2
- package/build/providers/authz_provider.d.ts.map +1 -1
- package/build/providers/authz_provider.js +48 -2
- package/build/providers/authz_provider.js.map +1 -1
- package/build/services/relations.d.ts +14 -0
- package/build/services/relations.d.ts.map +1 -0
- package/build/services/relations.js +17 -0
- package/build/services/relations.js.map +1 -0
- package/build/src/{catalog.d.ts → catalog/catalog.d.ts} +41 -2
- package/build/src/catalog/catalog.d.ts.map +1 -0
- package/build/src/{catalog.js → catalog/catalog.js} +120 -14
- package/build/src/catalog/catalog.js.map +1 -0
- package/build/src/{catalog_cache.d.ts → catalog/catalog_cache.d.ts} +46 -22
- package/build/src/catalog/catalog_cache.d.ts.map +1 -0
- package/build/src/{catalog_cache.js → catalog/catalog_cache.js} +53 -43
- package/build/src/catalog/catalog_cache.js.map +1 -0
- package/build/src/define_config.d.ts +101 -3
- package/build/src/define_config.d.ts.map +1 -1
- package/build/src/define_config.js.map +1 -1
- package/build/src/drivers/database_driver.d.ts +86 -4
- package/build/src/drivers/database_driver.d.ts.map +1 -1
- package/build/src/drivers/database_driver.js +425 -11
- package/build/src/drivers/database_driver.js.map +1 -1
- package/build/src/drivers/database_relations_driver.d.ts +75 -0
- package/build/src/drivers/database_relations_driver.d.ts.map +1 -0
- package/build/src/drivers/database_relations_driver.js +450 -0
- package/build/src/drivers/database_relations_driver.js.map +1 -0
- package/build/src/drivers/openfga_driver.d.ts +713 -119
- package/build/src/drivers/openfga_driver.d.ts.map +1 -1
- package/build/src/drivers/openfga_driver.js +2048 -476
- package/build/src/drivers/openfga_driver.js.map +1 -1
- package/build/src/drivers/openfga_facts.d.ts +369 -0
- package/build/src/drivers/openfga_facts.d.ts.map +1 -0
- package/build/src/drivers/openfga_facts.js +813 -0
- package/build/src/drivers/openfga_facts.js.map +1 -0
- package/build/src/drivers/openfga_relations_driver.d.ts +120 -0
- package/build/src/drivers/openfga_relations_driver.d.ts.map +1 -0
- package/build/src/drivers/openfga_relations_driver.js +466 -0
- package/build/src/drivers/openfga_relations_driver.js.map +1 -0
- package/build/src/errors.d.ts +258 -5
- package/build/src/errors.d.ts.map +1 -1
- package/build/src/errors.js +238 -7
- package/build/src/errors.js.map +1 -1
- package/build/src/freeze.d.ts +120 -0
- package/build/src/freeze.d.ts.map +1 -0
- package/build/src/freeze.js +172 -0
- package/build/src/freeze.js.map +1 -0
- package/build/src/http/app_access_middleware.d.ts.map +1 -0
- package/build/src/http/app_access_middleware.js.map +1 -0
- package/build/src/http/resource_access_middleware.d.ts +105 -0
- package/build/src/http/resource_access_middleware.d.ts.map +1 -0
- package/build/src/http/resource_access_middleware.js +81 -0
- package/build/src/http/resource_access_middleware.js.map +1 -0
- package/build/src/identity.d.ts +74 -1
- package/build/src/identity.d.ts.map +1 -1
- package/build/src/identity.js +100 -2
- package/build/src/identity.js.map +1 -1
- package/build/src/manager.d.ts +315 -4
- package/build/src/manager.d.ts.map +1 -1
- package/build/src/manager.js +1187 -181
- package/build/src/manager.js.map +1 -1
- package/build/src/models/authz_assignment.d.ts +6 -6
- package/build/src/models/authz_assignment.d.ts.map +1 -1
- package/build/src/models/authz_deny.d.ts +6 -6
- package/build/src/models/authz_deny.d.ts.map +1 -1
- package/build/src/models/authz_permission.d.ts +6 -6
- package/build/src/models/authz_permission.d.ts.map +1 -1
- package/build/src/models/authz_role.d.ts +6 -6
- package/build/src/models/authz_role.d.ts.map +1 -1
- package/build/src/models/authz_role_permission.d.ts +6 -6
- package/build/src/models/authz_role_permission.d.ts.map +1 -1
- package/build/src/openfga.d.ts +18 -2
- package/build/src/openfga.d.ts.map +1 -1
- package/build/src/openfga.js +15 -1
- package/build/src/openfga.js.map +1 -1
- package/build/src/reconcile.d.ts +37 -0
- package/build/src/reconcile.d.ts.map +1 -0
- package/build/src/reconcile.js +69 -0
- package/build/src/reconcile.js.map +1 -0
- package/build/src/relation_partition_trigger.d.ts +8 -0
- package/build/src/relation_partition_trigger.d.ts.map +1 -0
- package/build/src/relation_partition_trigger.js +85 -0
- package/build/src/relation_partition_trigger.js.map +1 -0
- package/build/src/relations/define_relations_config.d.ts +58 -0
- package/build/src/relations/define_relations_config.d.ts.map +1 -0
- package/build/src/relations/define_relations_config.js +144 -0
- package/build/src/relations/define_relations_config.js.map +1 -0
- package/build/src/relations/manager.d.ts +38 -0
- package/build/src/relations/manager.d.ts.map +1 -0
- package/build/src/relations/manager.js +156 -0
- package/build/src/relations/manager.js.map +1 -0
- package/build/src/relations/reconcile.d.ts +62 -0
- package/build/src/relations/reconcile.d.ts.map +1 -0
- package/build/src/relations/reconcile.js +138 -0
- package/build/src/relations/reconcile.js.map +1 -0
- package/build/src/relations_config_store.d.ts +22 -0
- package/build/src/relations_config_store.d.ts.map +1 -0
- package/build/src/relations_config_store.js +74 -0
- package/build/src/relations_config_store.js.map +1 -0
- package/build/src/scope_outbox.d.ts +69 -0
- package/build/src/scope_outbox.d.ts.map +1 -0
- package/build/src/scope_outbox.js +291 -0
- package/build/src/scope_outbox.js.map +1 -0
- package/build/src/{drivers → shared}/backend_guard.d.ts +14 -0
- package/build/src/shared/backend_guard.d.ts.map +1 -0
- package/build/src/{drivers → shared}/backend_guard.js +26 -1
- package/build/src/shared/backend_guard.js.map +1 -0
- package/build/src/shared/sql_expiry.d.ts.map +1 -0
- package/build/src/shared/sql_expiry.js.map +1 -0
- package/build/src/sql_descendants.d.ts +47 -1
- package/build/src/sql_descendants.d.ts.map +1 -1
- package/build/src/sql_descendants.js +75 -1
- package/build/src/sql_descendants.js.map +1 -1
- package/build/src/testing/contract.d.ts +74 -0
- package/build/src/testing/contract.d.ts.map +1 -1
- package/build/src/testing/contract.js +672 -169
- package/build/src/testing/contract.js.map +1 -1
- package/build/src/testing/main.d.ts +6 -0
- package/build/src/testing/main.d.ts.map +1 -1
- package/build/src/testing/main.js +3 -0
- package/build/src/testing/main.js.map +1 -1
- package/build/src/testing/migration_contract.d.ts +284 -0
- package/build/src/testing/migration_contract.d.ts.map +1 -0
- package/build/src/testing/migration_contract.js +586 -0
- package/build/src/testing/migration_contract.js.map +1 -0
- package/build/src/testing/relations_contract.d.ts +51 -0
- package/build/src/testing/relations_contract.d.ts.map +1 -0
- package/build/src/testing/relations_contract.js +654 -0
- package/build/src/testing/relations_contract.js.map +1 -0
- package/build/src/testing/relations_reconcile_contract.d.ts +24 -0
- package/build/src/testing/relations_reconcile_contract.d.ts.map +1 -0
- package/build/src/testing/relations_reconcile_contract.js +172 -0
- package/build/src/testing/relations_reconcile_contract.js.map +1 -0
- package/build/src/traits/authz_scopes.js +1 -1
- package/build/src/traits/authz_scopes.js.map +1 -1
- package/build/src/traits/has_uuid.d.ts +7 -7
- package/build/src/traits/has_uuid.d.ts.map +1 -1
- package/build/src/types.d.ts +865 -82
- package/build/src/types.d.ts.map +1 -1
- package/build/src/types.js +10 -0
- package/build/src/types.js.map +1 -1
- package/build/stubs/config/authorization.stub +104 -4
- package/build/stubs/migration.stub +126 -0
- package/build/stubs/scopes_outbox_migration.stub +57 -0
- package/package.json +4 -2
- package/build/commands/openfga_import.d.ts +0 -34
- package/build/commands/openfga_import.d.ts.map +0 -1
- package/build/commands/openfga_import.js +0 -97
- package/build/commands/openfga_import.js.map +0 -1
- package/build/src/catalog.d.ts.map +0 -1
- package/build/src/catalog.js.map +0 -1
- package/build/src/catalog_cache.d.ts.map +0 -1
- package/build/src/catalog_cache.js.map +0 -1
- package/build/src/drivers/backend_guard.d.ts.map +0 -1
- package/build/src/drivers/backend_guard.js.map +0 -1
- package/build/src/drivers/sql_expiry.d.ts.map +0 -1
- package/build/src/drivers/sql_expiry.js.map +0 -1
- package/build/src/middleware/app_access_middleware.d.ts.map +0 -1
- package/build/src/middleware/app_access_middleware.js.map +0 -1
- /package/build/src/{middleware → http}/app_access_middleware.d.ts +0 -0
- /package/build/src/{middleware → http}/app_access_middleware.js +0 -0
- /package/build/src/{drivers → shared}/sql_expiry.d.ts +0 -0
- /package/build/src/{drivers → shared}/sql_expiry.js +0 -0
|
@@ -0,0 +1,586 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* **El contrato de MIGRACIÓN** (3b-3b · B3) — la pieza que convierte «migra»
|
|
3
|
+
* en «migra sin perder nada que no esté declarado».
|
|
4
|
+
*
|
|
5
|
+
* import { runMigrationContract } from '@jantstack/adonis-authz/testing'
|
|
6
|
+
*
|
|
7
|
+
* Se publica junto al resto de la suite de contrato porque un driver de
|
|
8
|
+
* TERCEROS tiene que poder correrlo: el paquete promete migración idempotente
|
|
9
|
+
* y bidireccional entre drivers, y esa promesa no la puede sostener un test
|
|
10
|
+
* privado del paquete.
|
|
11
|
+
*
|
|
12
|
+
* Cómo funciona, y por qué así:
|
|
13
|
+
*
|
|
14
|
+
* 1. **Siembra FIJA** — 7 nodos, 6 holders, 4 roles, 14 grants, 5
|
|
15
|
+
* caducidades y 6 denies, escritos siempre con la API del driver. Fija a
|
|
16
|
+
* propósito: un fixture que cambia con cada corrida no permite comparar
|
|
17
|
+
* dos versiones del paquete, y el juez del panel 2 pedía «un set fijo de
|
|
18
|
+
* preguntas».
|
|
19
|
+
* 2. **448 preguntas idénticas** en origen y destino: 168 `authorize`
|
|
20
|
+
* (6 holders × 7 scopes × 4 permisos), 168 `hasRole` (× 4 roles), 42
|
|
21
|
+
* `listRoles`, 24 `listScopes`, 28 `listSubjects` y 18 `listRoleScopes`.
|
|
22
|
+
* Se preguntan ANTES de migrar sobre el origen y DESPUÉS sobre el
|
|
23
|
+
* destino, y las respuestas tienen que coincidir.
|
|
24
|
+
* 3. **`expectedLosses`** — la migración **no es sin pérdida** (panel 2,
|
|
25
|
+
* requisito 3 del dueño: *«posible con pérdidas declaradas y con
|
|
26
|
+
* ventana»*), así que las pérdidas se declaran DE ANTEMANO y el contrato
|
|
27
|
+
* las cruza en las dos direcciones:
|
|
28
|
+
* - una pérdida declarada tiene que aparecer **contada en
|
|
29
|
+
* `report.skipped`**; si no aparece, el contrato FALLA (una pérdida
|
|
30
|
+
* que nunca ocurre es una línea falsa en el README);
|
|
31
|
+
* - una respuesta que cambia y que NINGUNA pérdida declarada explica
|
|
32
|
+
* hace FALLAR el contrato. Ese es todo el punto: **nunca se ignora
|
|
33
|
+
* ninguna**.
|
|
34
|
+
* 4. **El CENSO de la siembra** (lote 3b-4 · C1) — y esto es lo que hace
|
|
35
|
+
* que el punto 3 sea una garantía y no un formulario. Las dos caras de
|
|
36
|
+
* `expectedLosses` cruzan `Object.keys(report.skipped)`, o sea **lo que
|
|
37
|
+
* el driver se auto-declara**: cierran las omisiones DESCUIDADAS, no las
|
|
38
|
+
* silenciosas. Un driver que tira un hecho sin contarlo no puebla
|
|
39
|
+
* `skipped` y, si esa pérdida no mueve ninguna de las 448, pasaba.
|
|
40
|
+
* Medido, y no es teórico: los denies solo se observan por `authorize`,
|
|
41
|
+
* que no distingue un deny en su sitio de uno reubicado a otro scope de
|
|
42
|
+
* la misma cadena.
|
|
43
|
+
* El censo no le pregunta al driver qué se dejó: mira el destino **hecho
|
|
44
|
+
* a hecho** (los 20 de la siembra: 14 asignaciones + 6 denies) por el
|
|
45
|
+
* camino DIRECTO del puerto —`listRoles`/`listDenies`, invariante 7— y
|
|
46
|
+
* una ausencia sin motivo declarado **y contado** hace FALLAR el
|
|
47
|
+
* contrato. `listDenies` es opcional en el puerto: un driver que no lo
|
|
48
|
+
* traiga deja sus denies observados solo por `authorize`, y el veredicto
|
|
49
|
+
* lo dice en `censusLimits` en vez de callárselo.
|
|
50
|
+
* 5. **El CRUCE DE CADUCIDADES** — ninguna de las 448 devuelve un
|
|
51
|
+
* `expiresAt` y el contrato no adelanta ningún reloj, así que perder la
|
|
52
|
+
* caducidad de un grant VIVO (convertirlo en permanente: fail-OPEN) era
|
|
53
|
+
* invisible. Se cruza por el único camino de solo-lectura-efectiva que
|
|
54
|
+
* hay en el puerto: `grant` con `expiresAt` OMITIDO devuelve
|
|
55
|
+
* `previousExpiresAt` (invariante 10).
|
|
56
|
+
* 6. **Tres combinaciones**: ida, vuelta e ida-y-vuelta (con `--prune`, que
|
|
57
|
+
* es la que demuestra que el viaje redondo no inventa ni pierde).
|
|
58
|
+
*
|
|
59
|
+
* **El límite de `a→b→a`, dicho en voz alta** (tester Fase 3b · M3): las 448
|
|
60
|
+
* de la vuelta se le preguntan al ORIGEN, y **nadie lo vacía** entre los dos
|
|
61
|
+
* tramos. Así que esa combinación observa lo que el tramo de vuelta DESTRUYE
|
|
62
|
+
* (un `--prune` que se lleva de más) y **no** lo que deja de escribir: con el
|
|
63
|
+
* origen ya lleno, «todo `unchanged`» es la respuesta correcta y un tramo
|
|
64
|
+
* omisivo es indistinguible de uno bueno. Quien migra de verdad (vacía el
|
|
65
|
+
* origen y lo reconstruye) está cubierto por las combinaciones `a→b` y `b→a`,
|
|
66
|
+
* que sí preguntan al destino recién llenado. Cerrar el hueco pide un gancho
|
|
67
|
+
* nuevo en el harness («vacía los hechos de este extremo»), que es una
|
|
68
|
+
* decisión del dueño y no se toma aquí.
|
|
69
|
+
*
|
|
70
|
+
* El motor está separado del registro de casos (`runMigrationDirection` /
|
|
71
|
+
* `runMigrationContract`) para que el propio paquete pueda comprobar que el
|
|
72
|
+
* contrato **sabe fallar**: un contrato que no falla no es una garantía.
|
|
73
|
+
*/
|
|
74
|
+
import { test } from '@japa/runner';
|
|
75
|
+
import { v7 as uuidv7 } from 'uuid';
|
|
76
|
+
import { APP_SCOPE } from '../types.js';
|
|
77
|
+
import { scopeKey } from '../identity.js';
|
|
78
|
+
import { memoryScopeTree } from './scope_tree.js';
|
|
79
|
+
/* ── La siembra FIJA ─────────────────────────────────────────────────────── */
|
|
80
|
+
/** Los 4 permisos del contrato de migración. `admin:all` no lo lleva ningún rol: hay preguntas que son `false` en todas partes. */
|
|
81
|
+
export const MIGRATION_PERMISSIONS = ['docs:read', 'docs:write', 'billing:read', 'admin:all'];
|
|
82
|
+
/** Los 4 roles, uno por nivel útil (dos a `app`, para que el nivel no sea la única variable). */
|
|
83
|
+
export const MIGRATION_ROLES = ['mig-editor', 'mig-viewer', 'mig-lead', 'mig-auditor'];
|
|
84
|
+
export const MIGRATION_CATALOG = {
|
|
85
|
+
permissions: MIGRATION_PERMISSIONS.map((slug) => ({ slug })),
|
|
86
|
+
roles: [
|
|
87
|
+
{ slug: 'mig-editor', scopeType: 'app', permissions: ['docs:read', 'docs:write'] },
|
|
88
|
+
{ slug: 'mig-viewer', scopeType: 'organization', permissions: ['docs:read'] },
|
|
89
|
+
{ slug: 'mig-lead', scopeType: 'unit', permissions: ['docs:read', 'docs:write', 'billing:read'] },
|
|
90
|
+
{ slug: 'mig-auditor', scopeType: 'app', permissions: ['billing:read'] },
|
|
91
|
+
],
|
|
92
|
+
};
|
|
93
|
+
export function makeMigrationSeed(now = new Date()) {
|
|
94
|
+
const shared = uuidv7();
|
|
95
|
+
return {
|
|
96
|
+
scopes: {
|
|
97
|
+
app: APP_SCOPE,
|
|
98
|
+
org1: { type: 'organization', uuid: uuidv7() },
|
|
99
|
+
org2: { type: 'organization', uuid: uuidv7() },
|
|
100
|
+
unit1: { type: 'unit', uuid: uuidv7() },
|
|
101
|
+
unit2: { type: 'unit', uuid: uuidv7() },
|
|
102
|
+
unit3: { type: 'unit', uuid: uuidv7() },
|
|
103
|
+
unit4: { type: 'unit', uuid: uuidv7() },
|
|
104
|
+
},
|
|
105
|
+
holders: [
|
|
106
|
+
{ type: 'users', uuid: uuidv7() },
|
|
107
|
+
{ type: 'users', uuid: uuidv7() },
|
|
108
|
+
{ type: 'users', uuid: uuidv7() },
|
|
109
|
+
{ type: 'users', uuid: shared },
|
|
110
|
+
// Invariante 4: el MISMO uuid con otro morph name jamás se cruza.
|
|
111
|
+
{ type: 'admins', uuid: shared },
|
|
112
|
+
{ type: 'admins', uuid: uuidv7() },
|
|
113
|
+
],
|
|
114
|
+
now,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
/** El árbol: 7 nodos con profundidad 4 (`app → org1 → unit1 → unit4`). */
|
|
118
|
+
export async function plantMigrationTree(tree, seed) {
|
|
119
|
+
const s = seed.scopes;
|
|
120
|
+
await tree.attach(s.org1, s.app);
|
|
121
|
+
await tree.attach(s.org2, s.app);
|
|
122
|
+
await tree.attach(s.unit1, s.org1);
|
|
123
|
+
await tree.attach(s.unit2, s.org1);
|
|
124
|
+
await tree.attach(s.unit3, s.org2);
|
|
125
|
+
await tree.attach(s.unit4, s.unit1);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* **Los 14 grants, en una tabla** — para que la siembra y el CRUCE DE
|
|
129
|
+
* CADUCIDADES salgan del mismo sitio: dos listas paralelas se separan al
|
|
130
|
+
* primer cambio y el cruce dejaría de comprobar lo que se sembró.
|
|
131
|
+
*/
|
|
132
|
+
export function migrationGrants(seed) {
|
|
133
|
+
const s = seed.scopes;
|
|
134
|
+
const [u1, u2, u3, u4, a1, a2] = seed.holders;
|
|
135
|
+
const soon = new Date(seed.now.getTime() + 3_600_000);
|
|
136
|
+
const later = new Date(seed.now.getTime() + 7_200_000);
|
|
137
|
+
const past = new Date(seed.now.getTime() - 3_600_000);
|
|
138
|
+
return [
|
|
139
|
+
{ holder: u1, role: 'mig-editor', scope: s.app },
|
|
140
|
+
{ holder: u1, role: 'mig-viewer', scope: s.org2 },
|
|
141
|
+
{ holder: u2, role: 'mig-viewer', scope: s.org1 },
|
|
142
|
+
{ holder: u2, role: 'mig-lead', scope: s.unit3, expiresAt: soon },
|
|
143
|
+
{ holder: u3, role: 'mig-lead', scope: s.unit1 },
|
|
144
|
+
{ holder: u3, role: 'mig-lead', scope: s.unit2, expiresAt: later },
|
|
145
|
+
// La caducada: existe en el origen y NO concede. Es la pérdida `expired`.
|
|
146
|
+
{ holder: u3, role: 'mig-auditor', scope: s.app, expiresAt: past, expired: true },
|
|
147
|
+
{ holder: u4, role: 'mig-lead', scope: s.unit4 },
|
|
148
|
+
{ holder: u4, role: 'mig-viewer', scope: s.org1, expiresAt: later },
|
|
149
|
+
{ holder: a1, role: 'mig-editor', scope: s.app },
|
|
150
|
+
{ holder: a1, role: 'mig-lead', scope: s.unit1 },
|
|
151
|
+
{ holder: a2, role: 'mig-viewer', scope: s.org2 },
|
|
152
|
+
{ holder: a2, role: 'mig-auditor', scope: s.app },
|
|
153
|
+
{ holder: u2, role: 'mig-lead', scope: s.unit4, expiresAt: soon },
|
|
154
|
+
];
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* **Los 6 denies, en una tabla** — misma razón que los grants: el CENSO
|
|
158
|
+
* (abajo) tiene que preguntar por lo que se sembró, y dos listas paralelas se
|
|
159
|
+
* separan al primer cambio.
|
|
160
|
+
*/
|
|
161
|
+
export function migrationDenies(seed) {
|
|
162
|
+
const s = seed.scopes;
|
|
163
|
+
const [u1, u2, u3, u4, a1, a2] = seed.holders;
|
|
164
|
+
return [
|
|
165
|
+
{ holder: u1, permission: 'docs:write', scope: s.org2 },
|
|
166
|
+
{ holder: u2, permission: 'docs:read', scope: s.unit3 },
|
|
167
|
+
{ holder: u3, permission: 'docs:write', scope: s.unit1 },
|
|
168
|
+
{ holder: u4, permission: 'billing:read', scope: s.app },
|
|
169
|
+
{ holder: a1, permission: 'docs:read', scope: s.unit1 },
|
|
170
|
+
{ holder: a2, permission: 'billing:read', scope: s.org2 },
|
|
171
|
+
];
|
|
172
|
+
}
|
|
173
|
+
export async function plantMigrationFacts(driver, seed) {
|
|
174
|
+
for (const g of migrationGrants(seed)) {
|
|
175
|
+
await driver.grant(g.holder, g.role, g.scope, g.expiresAt ? { expiresAt: g.expiresAt } : {});
|
|
176
|
+
}
|
|
177
|
+
for (const d of migrationDenies(seed)) {
|
|
178
|
+
await driver.deny(d.holder, d.permission, d.scope);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/** Los 20 hechos de la siembra (14 asignaciones + 6 denies), en un solo sitio. */
|
|
182
|
+
export function migrationFacts(seed) {
|
|
183
|
+
const facts = migrationGrants(seed).map((g) => ({
|
|
184
|
+
kind: 'assignment',
|
|
185
|
+
holder: g.holder,
|
|
186
|
+
scope: g.scope,
|
|
187
|
+
name: g.role,
|
|
188
|
+
// La caducada NO llega al destino, y eso está declarado: es la pérdida
|
|
189
|
+
// `expired`. Cualquier otra ausencia es un fallo.
|
|
190
|
+
...(g.expired ? { expectedLoss: 'expired' } : {}),
|
|
191
|
+
}));
|
|
192
|
+
for (const d of migrationDenies(seed)) {
|
|
193
|
+
facts.push({ kind: 'deny', holder: d.holder, scope: d.scope, name: d.permission });
|
|
194
|
+
}
|
|
195
|
+
return facts;
|
|
196
|
+
}
|
|
197
|
+
/** Cómo se nombra un hecho en el informe del contrato. */
|
|
198
|
+
export function describeMigrationFact(fact) {
|
|
199
|
+
return `${fact.kind === 'deny' ? 'deny' : 'grant'} ${tag(fact.holder)} ${fact.name} @ ${scopeTag(fact.scope)}`;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* **El censo** (lote 3b-4 · C1): pregunta al destino, hecho a hecho, si lo
|
|
203
|
+
* que se sembró está. Se lee por el camino DIRECTO del puerto —invariante 7,
|
|
204
|
+
* hechos directos del scope exacto—: `listRoles` para las asignaciones y
|
|
205
|
+
* `listDenies` para los denies. No mira `report.skipped`, no compara
|
|
206
|
+
* respuestas y no depende de que la pérdida cambie ninguna de las 448.
|
|
207
|
+
*
|
|
208
|
+
* `listDenies` es OPCIONAL en el puerto: un driver que no lo trae deja sus
|
|
209
|
+
* denies censados solo por `authorize` (lo dice `limits`, y está escrito en
|
|
210
|
+
* el README). Los dos drivers del paquete lo implementan.
|
|
211
|
+
*/
|
|
212
|
+
export async function censusMigrationFacts(driver, seed) {
|
|
213
|
+
const present = [];
|
|
214
|
+
const missing = [];
|
|
215
|
+
const limits = [];
|
|
216
|
+
let denyReadable = typeof driver.listDenies === 'function';
|
|
217
|
+
if (!denyReadable) {
|
|
218
|
+
limits.push('este driver no implementa `listDenies`, así que los 6 denies de la siembra no se pueden ' +
|
|
219
|
+
'censar uno a uno: solo los observa `authorize`, que no los ve donde no bloquean nada.');
|
|
220
|
+
}
|
|
221
|
+
for (const fact of migrationFacts(seed)) {
|
|
222
|
+
if (fact.kind === 'assignment') {
|
|
223
|
+
const roles = await driver.listRoles(fact.holder, fact.scope);
|
|
224
|
+
(roles.includes(fact.name) ? present : missing).push(fact);
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
if (!denyReadable)
|
|
228
|
+
continue;
|
|
229
|
+
try {
|
|
230
|
+
const denies = await driver.listDenies(fact.holder, fact.scope);
|
|
231
|
+
(denies.some((deny) => deny.permission === fact.name) ? present : missing).push(fact);
|
|
232
|
+
}
|
|
233
|
+
catch (error) {
|
|
234
|
+
// `listDenies: false` declarado con 500 `E_AUTHZ_UNSUPPORTED`: es la
|
|
235
|
+
// misma respuesta que no traerlo, y se dice igual.
|
|
236
|
+
if (error?.code !== 'E_AUTHZ_UNSUPPORTED')
|
|
237
|
+
throw error;
|
|
238
|
+
denyReadable = false;
|
|
239
|
+
limits.push('este driver responde 500 `E_AUTHZ_UNSUPPORTED` a `listDenies`, así que los denies de la ' +
|
|
240
|
+
'siembra no se censan uno a uno: solo los observa `authorize`.');
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return { present, missing, limits };
|
|
244
|
+
}
|
|
245
|
+
/* ── Las 448 preguntas ───────────────────────────────────────────────────── */
|
|
246
|
+
function tag(subject) {
|
|
247
|
+
return `${subject.type}:${subject.uuid}`;
|
|
248
|
+
}
|
|
249
|
+
function scopeTag(scope) {
|
|
250
|
+
return scopeKey(scope);
|
|
251
|
+
}
|
|
252
|
+
function scopeList(scopes) {
|
|
253
|
+
return scopes.map(scopeTag).sort().join(',');
|
|
254
|
+
}
|
|
255
|
+
function subjectList(subjects) {
|
|
256
|
+
return subjects.map(tag).sort().join(',');
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Las 448: **168 + 168 + 42 + 24 + 28 + 18**. No es un número redondo por
|
|
260
|
+
* casualidad — es el producto de la siembra fija, y por eso el contrato
|
|
261
|
+
* puede afirmar «448 preguntas idénticas» y no «unas cuantas».
|
|
262
|
+
*/
|
|
263
|
+
export function migrationQuestions(seed) {
|
|
264
|
+
const s = seed.scopes;
|
|
265
|
+
const scopes = [s.app, s.org1, s.org2, s.unit1, s.unit2, s.unit3, s.unit4];
|
|
266
|
+
const holders = seed.holders;
|
|
267
|
+
const questions = [];
|
|
268
|
+
for (const holder of holders) {
|
|
269
|
+
for (const scope of scopes) {
|
|
270
|
+
for (const permission of MIGRATION_PERMISSIONS) {
|
|
271
|
+
questions.push({
|
|
272
|
+
id: `authorize(${tag(holder)}, ${permission}, ${scopeTag(scope)})`,
|
|
273
|
+
kind: 'authorize',
|
|
274
|
+
ask: async (driver) => String(await driver.authorize(holder, permission, scope)),
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
for (const holder of holders) {
|
|
280
|
+
for (const scope of scopes) {
|
|
281
|
+
for (const role of MIGRATION_ROLES) {
|
|
282
|
+
questions.push({
|
|
283
|
+
id: `hasRole(${tag(holder)}, ${role}, ${scopeTag(scope)})`,
|
|
284
|
+
kind: 'hasRole',
|
|
285
|
+
ask: async (driver) => String(await driver.hasRole(holder, role, scope)),
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
for (const holder of holders) {
|
|
291
|
+
for (const scope of scopes) {
|
|
292
|
+
questions.push({
|
|
293
|
+
id: `listRoles(${tag(holder)}, ${scopeTag(scope)})`,
|
|
294
|
+
kind: 'listRoles',
|
|
295
|
+
ask: async (driver) => [...(await driver.listRoles(holder, scope))].sort().join(','),
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
for (const holder of holders) {
|
|
300
|
+
for (const permission of MIGRATION_PERMISSIONS) {
|
|
301
|
+
questions.push({
|
|
302
|
+
id: `listScopes(${tag(holder)}, ${permission})`,
|
|
303
|
+
kind: 'listScopes',
|
|
304
|
+
ask: async (driver) => scopeList(await driver.listScopes(holder, permission)),
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
for (const role of MIGRATION_ROLES) {
|
|
309
|
+
for (const scope of scopes) {
|
|
310
|
+
questions.push({
|
|
311
|
+
id: `listSubjects(${role}, ${scopeTag(scope)})`,
|
|
312
|
+
kind: 'listSubjects',
|
|
313
|
+
ask: async (driver) => subjectList(await driver.listSubjects(role, scope)),
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
for (const holder of holders) {
|
|
318
|
+
for (const scopeType of ['app', 'organization', 'unit']) {
|
|
319
|
+
questions.push({
|
|
320
|
+
id: `listRoleScopes(${tag(holder)}, ${scopeType})`,
|
|
321
|
+
kind: 'listRoleScopes',
|
|
322
|
+
ask: async (driver) => scopeList(await driver.listRoleScopes(holder, scopeType)),
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return questions;
|
|
327
|
+
}
|
|
328
|
+
/** Cuántas preguntas tiene el set fijo. Es una CIFRA del contrato, no un detalle. */
|
|
329
|
+
export const MIGRATION_QUESTION_COUNT = 448;
|
|
330
|
+
function reasonsOf(reports) {
|
|
331
|
+
const out = new Set();
|
|
332
|
+
for (const report of reports)
|
|
333
|
+
for (const reason of Object.keys(report.skipped))
|
|
334
|
+
out.add(reason);
|
|
335
|
+
return out;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Corre UNA combinación de punta a punta y devuelve el veredicto. Es el
|
|
339
|
+
* motor: `runMigrationContract` solo lo envuelve en tres casos de Japa, y el
|
|
340
|
+
* paquete lo usa directamente para demostrar que el contrato **sabe fallar**.
|
|
341
|
+
*/
|
|
342
|
+
export async function runMigrationDirection(harness, direction) {
|
|
343
|
+
await harness.cleanup();
|
|
344
|
+
await harness.seedCatalog(MIGRATION_CATALOG);
|
|
345
|
+
const tree = harness.makeTree ? await harness.makeTree() : memoryScopeTree();
|
|
346
|
+
const { reconcile, drivers } = await harness.setup(tree);
|
|
347
|
+
const seed = makeMigrationSeed();
|
|
348
|
+
await plantMigrationTree(tree, seed);
|
|
349
|
+
const origin = direction === 'b→a' ? harness.b : harness.a;
|
|
350
|
+
const other = origin === harness.a ? harness.b : harness.a;
|
|
351
|
+
await plantMigrationFacts(drivers[origin], seed);
|
|
352
|
+
const questions = migrationQuestions(seed);
|
|
353
|
+
const before = new Map();
|
|
354
|
+
for (const question of questions)
|
|
355
|
+
before.set(question.id, await question.ask(drivers[origin]));
|
|
356
|
+
const reports = [];
|
|
357
|
+
const failures = [];
|
|
358
|
+
const mismatches = [];
|
|
359
|
+
const explains = (question) => harness.expectedLosses.find((candidate) => (candidate.directions ?? ['a→b', 'b→a', 'a→b→a']).includes(direction) &&
|
|
360
|
+
candidate.changesAnswer?.(question) === true) ?? null;
|
|
361
|
+
/** Compara las 448 contra UN extremo y apunta lo que cambió sin explicación. */
|
|
362
|
+
const compareAgainst = async (driverKey, label) => {
|
|
363
|
+
for (const question of questions) {
|
|
364
|
+
const after = await question.ask(drivers[driverKey]);
|
|
365
|
+
const was = before.get(question.id);
|
|
366
|
+
if (after === was)
|
|
367
|
+
continue;
|
|
368
|
+
const loss = explains(question);
|
|
369
|
+
const id = label ? `${label} ${question.id}` : question.id;
|
|
370
|
+
mismatches.push({ question: id, before: was, after, explainedBy: loss?.reason ?? null });
|
|
371
|
+
if (!loss) {
|
|
372
|
+
failures.push(`[${direction}] la respuesta CAMBIÓ y no hay pérdida declarada que lo explique: ` +
|
|
373
|
+
`${id} — antes '${was}', después '${after}'`);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
/**
|
|
378
|
+
* **Un tramo tiene que HACER algo** (tester Fase 3b · M3). Sin esto, en
|
|
379
|
+
* `a→b→a` el tramo de vuelta podía no ejecutarse siquiera: como las
|
|
380
|
+
* preguntas se le hacen al ORIGEN —que nadie vacía—, un tramo de vuelta que
|
|
381
|
+
* no escribe, no borra y no lee NADA es indistinguible de uno correcto y
|
|
382
|
+
* las 448 salen idénticas. Medido: quitando el `reconcile` de vuelta la
|
|
383
|
+
* suite entera seguía en verde.
|
|
384
|
+
*/
|
|
385
|
+
const legDidSomething = (report, leg) => {
|
|
386
|
+
const seen = report.written + report.updated + report.unchanged + report.extra + report.deleted;
|
|
387
|
+
if (seen > 0)
|
|
388
|
+
return;
|
|
389
|
+
failures.push(`[${direction}] el tramo ${leg} no tocó NI UNA tupla (written/updated/unchanged/extra/deleted todo a cero). ` +
|
|
390
|
+
`Una migración que no mira nada no es una migración: comprueba el origen, el destino y el --from.`);
|
|
391
|
+
};
|
|
392
|
+
/* ── El CENSO de la siembra (lote 3b-4 · C1) ─────────────────────────
|
|
393
|
+
*
|
|
394
|
+
* `expectedLosses` cruzaba `Object.keys(report.skipped)`, o sea **lo que el
|
|
395
|
+
* propio driver se auto-declara**: cerraba las omisiones DESCUIDADAS y no
|
|
396
|
+
* las silenciosas. Un driver que tira un hecho sin contarlo no puebla
|
|
397
|
+
* `skipped`, y si esa pérdida no mueve ninguna de las 448 —los denies solo
|
|
398
|
+
* se observan por `authorize`, y `authorize` no ve un deny reubicado dentro
|
|
399
|
+
* de la misma cadena— el contrato pasaba.
|
|
400
|
+
*
|
|
401
|
+
* El censo no pregunta al driver qué se dejó: **mira el destino hecho a
|
|
402
|
+
* hecho** por el camino DIRECTO del puerto y compara con lo que el propio
|
|
403
|
+
* contrato sembró. Una ausencia sin motivo DECLARADO **y CONTADO** es un
|
|
404
|
+
* fallo, cuente el driver lo que cuente.
|
|
405
|
+
*/
|
|
406
|
+
const silentLosses = [];
|
|
407
|
+
const censusLimits = [];
|
|
408
|
+
const censusAt = async (driverKey, label) => {
|
|
409
|
+
const census = await censusMigrationFacts(drivers[driverKey], seed);
|
|
410
|
+
for (const limit of census.limits)
|
|
411
|
+
if (!censusLimits.includes(limit))
|
|
412
|
+
censusLimits.push(limit);
|
|
413
|
+
const seen = reasonsOf(reports);
|
|
414
|
+
const known = new Set(harness.expectedLosses
|
|
415
|
+
.filter((loss) => (loss.directions ?? ['a→b', 'b→a', 'a→b→a']).includes(direction))
|
|
416
|
+
.map((loss) => loss.reason));
|
|
417
|
+
for (const fact of census.missing) {
|
|
418
|
+
const id = describeMigrationFact(fact);
|
|
419
|
+
if (!fact.expectedLoss) {
|
|
420
|
+
silentLosses.push({ fact: id, at: driverKey });
|
|
421
|
+
failures.push(`[${direction}] PÉRDIDA SILENCIOSA: '${id}' se sembró y '${driverKey}'${label} no lo tiene. ` +
|
|
422
|
+
`No hay ninguna pérdida declarada que lo cubra y el driver TAMPOCO lo contó en report.skipped ` +
|
|
423
|
+
`(motivos vistos: ${[...seen].sort().join(', ') || 'ninguno'}). Que las 448 respuestas no se ` +
|
|
424
|
+
`muevan no lo salva: un hecho que desaparece sin contarse es exactamente lo que la migración ` +
|
|
425
|
+
`no puede hacer.`);
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
if (!known.has(fact.expectedLoss)) {
|
|
429
|
+
failures.push(`[${direction}] '${id}' no está en '${driverKey}'${label} y el contrato lo atribuye a la pérdida ` +
|
|
430
|
+
`'${fact.expectedLoss}', que este harness NO declara en expectedLosses.`);
|
|
431
|
+
continue;
|
|
432
|
+
}
|
|
433
|
+
if (!seen.has(fact.expectedLoss)) {
|
|
434
|
+
failures.push(`[${direction}] '${id}' no está en '${driverKey}'${label}: la pérdida '${fact.expectedLoss}' ` +
|
|
435
|
+
`OCURRIÓ y el driver no la contó en report.skipped. Perderla está declarado; perderla en ` +
|
|
436
|
+
`silencio, no.`);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
};
|
|
440
|
+
reports.push(await reconcile({ to: other, from: origin }));
|
|
441
|
+
legDidSomething(reports[0], `${origin} → ${other}`);
|
|
442
|
+
// La ida y vuelta lleva `--prune`: es la única forma de comprobar que el
|
|
443
|
+
// viaje redondo no deja de MÁS en el origen (y de ver que lo que se lleva
|
|
444
|
+
// por delante es exactamente una pérdida DECLARADA).
|
|
445
|
+
if (direction === 'a→b→a') {
|
|
446
|
+
// Y el INTERMEDIO también se pregunta: si no, el tramo de ida podría
|
|
447
|
+
// haberse dejado media siembra y la vuelta —que ni siquiera necesita
|
|
448
|
+
// escribir, porque el origen sigue lleno— lo taparía.
|
|
449
|
+
await compareAgainst(other, `[intermedio ${other}]`);
|
|
450
|
+
await censusAt(other, ' (intermedio)');
|
|
451
|
+
reports.push(await reconcile({ to: origin, from: other, prune: true }));
|
|
452
|
+
legDidSomething(reports[1], `${other} → ${origin} (--prune)`);
|
|
453
|
+
}
|
|
454
|
+
const asked = direction === 'a→b→a' ? origin : other;
|
|
455
|
+
await compareAgainst(asked, '');
|
|
456
|
+
await censusAt(asked, '');
|
|
457
|
+
// **Las dos caras, y las dos son obligatorias.**
|
|
458
|
+
//
|
|
459
|
+
// (i) Todo lo que la pasada dejó fuera —cada motivo de `report.skipped`—
|
|
460
|
+
// tiene que estar DECLARADO. Es la mitad que hace del contrato una
|
|
461
|
+
// garantía y no un adorno: sin ella, un driver puede saltarse lo que
|
|
462
|
+
// quiera mientras no cambie ninguna de las 448 respuestas de ESTA
|
|
463
|
+
// siembra, que es justo la clase de bug que una siembra fija no ve.
|
|
464
|
+
// «Nunca se ignora ninguna» significa esto.
|
|
465
|
+
const counted = reasonsOf(reports);
|
|
466
|
+
const declared = new Set(harness.expectedLosses
|
|
467
|
+
.filter((loss) => (loss.directions ?? ['a→b', 'b→a', 'a→b→a']).includes(direction))
|
|
468
|
+
.map((loss) => loss.reason));
|
|
469
|
+
for (const reason of [...counted].sort()) {
|
|
470
|
+
if (declared.has(reason))
|
|
471
|
+
continue;
|
|
472
|
+
failures.push(`[${direction}] la pasada NO migró algo por '${reason}' y esa pérdida no está declarada en ` +
|
|
473
|
+
`expectedLosses. Declárala (con su motivo medido) o arregla la migración: una pérdida que no ` +
|
|
474
|
+
`está escrita es una pérdida silenciosa.`);
|
|
475
|
+
}
|
|
476
|
+
// (ii) Y al revés: una pérdida declarada tiene que OCURRIR y salir contada.
|
|
477
|
+
// Si no, es una línea falsa en el README, y el README es el contrato
|
|
478
|
+
// con el consumidor.
|
|
479
|
+
const declaredButAbsent = [];
|
|
480
|
+
for (const loss of harness.expectedLosses) {
|
|
481
|
+
if (!(loss.directions ?? ['a→b', 'b→a', 'a→b→a']).includes(direction))
|
|
482
|
+
continue;
|
|
483
|
+
if (counted.has(loss.reason))
|
|
484
|
+
continue;
|
|
485
|
+
declaredButAbsent.push(loss.reason);
|
|
486
|
+
failures.push(`[${direction}] la pérdida declarada '${loss.reason}' NO aparece contada en report.skipped ` +
|
|
487
|
+
`(motivos vistos: ${[...counted].sort().join(', ') || 'ninguno'}). Una pérdida que nunca ocurre ` +
|
|
488
|
+
`no se declara: se borra.`);
|
|
489
|
+
}
|
|
490
|
+
/* ── El CRUCE DE CADUCIDADES (tester Fase 3b · M1) ────────────────────
|
|
491
|
+
*
|
|
492
|
+
* Ninguna de las 448 preguntas devuelve un `expiresAt`, y no hay reloj que
|
|
493
|
+
* adelantar: una migración que se deja por el camino la caducidad de un
|
|
494
|
+
* grant VIVO —lo convierte en permanente— responde exactamente lo mismo a
|
|
495
|
+
* las 448 y no cuenta nada en `skipped`. Medido: con
|
|
496
|
+
* `reconcile --to=database` escribiendo `expires_at: null`, las TRES
|
|
497
|
+
* combinaciones seguían en VERDE. Es la pérdida de datos que el contrato
|
|
498
|
+
* decía cortar y no cortaba, y además es fail-OPEN (un permiso temporal que
|
|
499
|
+
* ya no caduca).
|
|
500
|
+
*
|
|
501
|
+
* Se cruza por el ÚNICO camino del puerto que devuelve una caducidad sin
|
|
502
|
+
* inventar un método nuevo: `grant` con `expiresAt` OMITIDO devuelve
|
|
503
|
+
* `previousExpiresAt` (invariante 10, «omitido preserva»). Va DESPUÉS de
|
|
504
|
+
* las 448 —es una escritura, aunque idempotente y sin cambio de estado— y
|
|
505
|
+
* por eso no puede alterar ninguna respuesta ya medida.
|
|
506
|
+
*/
|
|
507
|
+
const expiryMismatches = [];
|
|
508
|
+
const stamp = (value) => value === undefined ? 'ausente' : value === null ? 'sin caducidad' : value.toISOString();
|
|
509
|
+
const crossExpiries = async (driverKey, label) => {
|
|
510
|
+
for (const g of migrationGrants(seed)) {
|
|
511
|
+
// La caducada es la pérdida declarada: en el destino no está, y `grant`
|
|
512
|
+
// con `{}` la REVIVIRÍA (invariante 10). No se cruza.
|
|
513
|
+
if (g.expired)
|
|
514
|
+
continue;
|
|
515
|
+
const outcome = await drivers[driverKey].grant(g.holder, g.role, g.scope, {});
|
|
516
|
+
const after = stamp(outcome.existed ? (outcome.previousExpiresAt ?? null) : undefined);
|
|
517
|
+
const was = stamp(g.expiresAt ?? null);
|
|
518
|
+
if (after === was)
|
|
519
|
+
continue;
|
|
520
|
+
const id = `${tag(g.holder)} ${g.role} @ ${scopeTag(g.scope)}`;
|
|
521
|
+
expiryMismatches.push({ grant: `${driverKey}${label} ${id}`, before: was, after });
|
|
522
|
+
failures.push(`[${direction}] la CADUCIDAD no sobrevivió a la migración: ${id} en '${driverKey}'${label} ` +
|
|
523
|
+
`— antes '${was}', después '${after}'. Ninguna de las ${MIGRATION_QUESTION_COUNT} ` +
|
|
524
|
+
`preguntas lo ve (no devuelven caducidades) y perderla es fail-OPEN: un permiso temporal que ya no caduca.`);
|
|
525
|
+
}
|
|
526
|
+
};
|
|
527
|
+
// El INTERMEDIO también se cruza (lote 3b-4 · C1): en `a→b→a` las 448 y el
|
|
528
|
+
// cruce se le hacen al ORIGEN, que nadie vacía, así que una caducidad que
|
|
529
|
+
// el tramo de IDA se dejó quedaba tapada. Se hace DESPUÉS del segundo
|
|
530
|
+
// tramo a propósito: `grant` con `{}` es una escritura y crearía en el
|
|
531
|
+
// intermedio el hecho que falta, falseando la pasada de vuelta.
|
|
532
|
+
if (direction === 'a→b→a')
|
|
533
|
+
await crossExpiries(other, ' (intermedio)');
|
|
534
|
+
await crossExpiries(asked, '');
|
|
535
|
+
// Los TRAMOS que la combinación promete: `a→b→a` son DOS pasadas, no una.
|
|
536
|
+
// Es la comprobación barata que delata un motor —o un harness— al que se le
|
|
537
|
+
// ha caído un tramo por el camino (tester Fase 3b · M3).
|
|
538
|
+
const expectedLegs = direction === 'a→b→a' ? 2 : 1;
|
|
539
|
+
if (reports.length !== expectedLegs) {
|
|
540
|
+
failures.push(`[${direction}] la combinación son ${expectedLegs} pasada(s) de reconcile y se ejecutaron ${reports.length}`);
|
|
541
|
+
}
|
|
542
|
+
if (questions.length !== MIGRATION_QUESTION_COUNT) {
|
|
543
|
+
failures.push(`[${direction}] el set fijo tiene ${questions.length} preguntas y no ${MIGRATION_QUESTION_COUNT}`);
|
|
544
|
+
}
|
|
545
|
+
return {
|
|
546
|
+
direction,
|
|
547
|
+
reports,
|
|
548
|
+
questions: questions.length,
|
|
549
|
+
mismatches,
|
|
550
|
+
declaredButAbsent,
|
|
551
|
+
expiryMismatches,
|
|
552
|
+
silentLosses,
|
|
553
|
+
censusLimits,
|
|
554
|
+
failures,
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* Registra las tres combinaciones como casos de Japa. Llámala en el TOP LEVEL
|
|
559
|
+
* del spec (Japa descarta los grupos registrados tarde).
|
|
560
|
+
*/
|
|
561
|
+
export function runMigrationContract(harness) {
|
|
562
|
+
return registerMigrationContract(harness, {
|
|
563
|
+
group: (title, define) => test.group(title, define),
|
|
564
|
+
test: (title, fn) => test(title, fn),
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
export function registerMigrationContract(harness, api) {
|
|
568
|
+
api.group(`migration contract [${harness.name}]`, (group) => {
|
|
569
|
+
group.teardown(async () => {
|
|
570
|
+
await harness.cleanup();
|
|
571
|
+
});
|
|
572
|
+
for (const direction of ['a→b', 'b→a', 'a→b→a']) {
|
|
573
|
+
const label = direction === 'a→b'
|
|
574
|
+
? `${harness.a} → ${harness.b}`
|
|
575
|
+
: direction === 'b→a'
|
|
576
|
+
? `${harness.b} → ${harness.a}`
|
|
577
|
+
: `${harness.a} → ${harness.b} → ${harness.a} (--prune)`;
|
|
578
|
+
api.test(`${MIGRATION_QUESTION_COUNT} preguntas idénticas, ${label}: las respuestas coinciden y toda pérdida está declarada`, async ({ assert }) => {
|
|
579
|
+
const verdict = await runMigrationDirection(harness, direction);
|
|
580
|
+
assert.equal(verdict.questions, MIGRATION_QUESTION_COUNT);
|
|
581
|
+
assert.deepEqual(verdict.failures, []);
|
|
582
|
+
})?.timeout(600_000);
|
|
583
|
+
}
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
//# sourceMappingURL=migration_contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration_contract.js","sourceRoot":"","sources":["../../../src/testing/migration_contract.ts"],"names":[],"mappings":"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"}
|