diff --git a/docs/spec/compat-tests.md b/docs/spec/compat-tests.md new file mode 100644 index 0000000..b3f1967 --- /dev/null +++ b/docs/spec/compat-tests.md @@ -0,0 +1,70 @@ +# Running the compat tests against a copy of INBUXA's data + +Status: 2026-09-19. + +Each feature spec has one **(compat)** acceptance test: the check that +INBUXA's own data opens in inbuxa-server and reads back as it did on the +Enterprise server (SPEC.md ยง7). They are written, `#[ignore]`d, and unrun, +because the repository has no copy of that data. + +**Run them against a copy, never against the live server.** Two of them +delete data as part of what they check: `monitoring_compat` purges the +telemetry history it has just read, and `per_domain_directory_compat` reads +only, but the monitoring one is enough reason to treat the whole set as +destructive. The brief bars touching INBUXA's production server, and these +tests are not an exception. + +## What you need + +1. A copy of the data store, opened with the same `STORE` backend the copy + was taken from, and a `TMPDIR` pointing at it. `NO_INSERT=1` stops the + harness from resetting or seeding it. +2. `INBUXA_COMPAT_ADMIN`, as `name:password`, for a server-level + administrator in that copy. +3. For `tenant_compat` only: `INBUXA_COMPAT_EXPECTED`, a JSON file recorded + from the Enterprise server before the move: + + ```json + { + "tenants": {"": {"name": "...", "quotas": {}, "members": [""]}}, + "tenantAdmins": {"": {"password": "...", "accounts": [""], + "domains": [""]}} + } + ``` + +## Running one + +``` +NO_INSERT=1 STORE= TMPDIR=/path/to/copy \ +INBUXA_COMPAT_ADMIN='admin@example.org:' \ +RUST_MIN_STACK=8388608 \ +cargo test -p tests --features -- --ignored --exact +``` + +`` is the full path, such as `system::tenant::tenant_compat`. +Run them one at a time: each starts a server on fixed ports. + +## The eight tests + +| Test | Feature | What it checks | +|---|---|---| +| `system::tenant::tenant_compat` | 1, multi-tenancy | Tenants, their members and quotas read back unchanged, and each tenant administrator sees what it saw before (needs `INBUXA_COMPAT_EXPECTED`) | +| `system::masked_email::masked_email_compat` | 2, masked email | Existing masked addresses still deliver, and their state reads back | +| `system::undelete::undelete_compat` | 3, undelete | Archived items are still listed and restorable | +| `system::branding::branding_compat` | 4, branding | Every domain's and tenant's logo, `logoUrl` and the three templates read back as stored | +| `system::ai::ai_compat` | 5, AI spam | The twelve `LLM_*` tags and their scores | +| `system::monitoring::monitoring_compat` | 6, monitoring | Retention, stores and `indexTelemetry` as observed; old history in the stripped encoding is skipped, not an error, and is gone after one purge (**deletes history**) | +| `scim::scim_compat` | 7, SCIM | No domain open to SCIM, and no account with an `externalId`, as observed | +| `directory::per_domain::per_domain_directory_compat` | 9, per-domain directories | No directory, no server default, and no domain with its own directory: any domain with one is a cutover blocker | + +## What a failure means + +- `tenant_compat`, `masked_email_compat`, `undelete_compat`, + `branding_compat`, `ai_compat`: the fork reads that data differently from + the Enterprise server. Treat as a cutover blocker and fix before moving. +- `monitoring_compat`: old telemetry that can't be decoded is expected and + is skipped; a failure here means the settings differ from what was + observed. +- `scim_compat` and `per_domain_directory_compat`: they assert what was + observed on 2026-09-18, that INBUXA uses neither feature. A failure means + it has started to, and that feature's cutover notes then apply. diff --git a/docs/spec/features/per-domain-directories.md b/docs/spec/features/per-domain-directories.md index 3b6bf6b..2f83f76 100644 --- a/docs/spec/features/per-domain-directories.md +++ b/docs/spec/features/per-domain-directories.md @@ -551,18 +551,18 @@ marked `inbuxa:`. - **Tests.** `directory::per_domain::per_domain_directory_tests` covers tests 1, 3, 4, 6, 7, 9, 11, 15 and 19, and DIR-20 and DIR-21, over SQL directories on SQLite files, with no container. `directory_tests` runs a - new `oidc` module in place of the removed one, against Keycloak: tests 5, - 8, 12, 13, 14, 16 and 17 in part (below). SCIM's acceptance test 5 + new `oidc` module in place of the removed one, against Keycloak, whose + container now imports a second realm: tests 5, 8, 10, 12, 13, 14, 16, 17 + and 18, the last two in part (below). SCIM's acceptance test 5 (`scim_oidc_tests`) now runs and passes. - **Test 20 (compat)** is `per_domain_directory_compat`, ignored, and unrun until a copy of INBUXA's data is provided. It checks observed 1. - **Not exercised, or only in part:** - Test 2 and test 9 use an SQL directory that can't open instead of a - stopped LDAP server, and test 18 (a stopped provider, and the sign-in - ban) isn't run: the Keycloak container is shared. - - Test 10 needs a second provider, and test 12's later sign-ins (an empty - groups claim clearing groups, a missing one keeping them) need changes - to Keycloak users; neither is run. + stopped LDAP server. + - Test 12's later sign-ins (an empty groups claim clearing groups, a + missing one keeping them) need changes to Keycloak users, and aren't + run. - Test 13 is checked through synchronization itself, since the realm's users aren't on the tenant's domain. Test 14 reuses an account an administrator made, not one from an earlier LDAP directory. @@ -570,7 +570,10 @@ marked `inbuxa:`. token. Keycloak grants every required scope whatever is asked, so the missing-scope refusal isn't reached; audience and key rotation aren't run. Test 17 checks password sign-in and a malformed token; an opaque - token and `usernameDomain` aren't run. + token that the provider accepts, and `usernameDomain`, aren't run. + Test 18 stops the provider and checks an outage doesn't ban the + client, and that bad tokens do; it doesn't measure the failure's + latency. - DIR-22's rule that `Authentication.directoryId` names a server-level directory, and DIR-24 (a tenant administrator setting its own domains' directory), aren't tested. @@ -583,6 +586,9 @@ marked `inbuxa:`. reload from applying. - A server default naming no directory is unavailable, like a domain's (DIR-5); it used to mean the internal directory. + - A token the OIDC directory refuses is an authentication failure, so it + counts toward the sign-in ban (DIR-30); it used to be an error, which + counts toward nothing. - **Known limits, not requirements of this spec:** - An SQL directory on a SQLite path that can't be opened holds the reload, and the request that caused it, for the pool's 30-second connection diff --git a/docs/spec/features/scale-out-storage.md b/docs/spec/features/scale-out-storage.md index 4567a93..672cf26 100644 --- a/docs/spec/features/scale-out-storage.md +++ b/docs/spec/features/scale-out-storage.md @@ -553,18 +553,19 @@ Decision). treats as two members. `store::replica::replica_tests`, built with `postgres` and run with `STORE=PostgreSqlReplicated`, runs a primary and a streaming hot standby in containers and covers tests 9, 10, 12, 13, 14 - and 15. Test 1 is the existing store, blob and protocol suites passing - unchanged. + and 15. `replica_cluster_tests` (with `redis`) covers test 11, and + `replica_mysql` covers tests 17 to 19 against two MySQL pairs, one + replicating with GTIDs and one by binary log position. Test 1 is the + existing store, blob and protocol suites passing unchanged. - **Not exercised:** test 3's downloads over JMAP and IMAP after a restart (the same blob reads are checked at the store), test 5's queued delivery (the failing write is checked), test 21 (one of two Redis servers stopped), the `resetRateLimiters` and `removeLock*` maintenance types (the store - operations they use are checked), test 11 (two nodes), test 16 (the - primary stopped), and tests 17 to 19: the MySQL code (GTID and - `Seconds_Behind_Source` lag, the read-only and commit-order checks) is - built but hasn't run against a MySQL replica. Test 9 checks that the - replica served the reads, not the replica's statement log, and test 15 - checks full-text search, not a SQL directory. + operations they use are checked), and test 16 (the primary stopped). + Test 9 checks that the replica served the reads, not the replica's + statement log, and test 15 checks full-text search, not a SQL directory. + Test 11 uses one server and a second replicated store with its own + marks, rather than two whole nodes. - **Settled from the code, not a change of intent:** - The FileSystem backend reports any unreadable file as missing, so a FileSystem member that can't be read looks like a miss (ST-17's search @@ -590,8 +591,9 @@ Decision). - Placement is the fork's own (ST-16), so an install coming from a sharded upstream deployment reads through ST-17's search (open question). - - ST-7's step 4 raises the mark from a JMAP `sinceState` only; IMAP - `CONDSTORE` and `QRESYNC` values and push resumption don't yet. + - ST-7's step 4 raises the mark from a JMAP `sinceState` and an IMAP + `FETCH ... CHANGEDSINCE`; a JMAP `queryChanges` state, `QRESYNC` on + SELECT and push resumption don't yet. - The store's `enterprise` Cargo feature stays: other crates' feature lists name it.