mirror of
https://github.com/mudler/LocalAI.git
synced 2026-11-04 11:33:22 -05:00
* feat(addons): add a durable host policy store Keep bounded host policy in one owner-managed durable store. Use revision checks and retained receipts to reconcile updates without stale writes. Require a sole migration source and explicit owner selection for clusters. Restrict provider pins to tools and skills that existing consumers replace. Document the internal scope; UI and carrier integration remain separate. Assisted-by: Codex:gpt-5 Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * fix(addons): reject malformed host policy JSON Reject duplicate keys, noncanonical fields, and incomplete durable structures before JSON decoding can discard evidence of corruption. Validate provider keys before deletion so invalid removals cannot commit. Keep empty provider sets canonical when writing migrated policies. Regressions cover malformed documents, unchanged storage on rejection, and replay with case-sensitive operation IDs. Assisted-by: OpenAI:ChatGPT Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * fix(addons): decouple host policy validation Move shared egress parsers into a dependency-light package so the gateway can consume host settings without importing itself through core/config. Keep config wrappers and a host type alias to preserve existing callers. Assisted-by: Codex:gpt-5 Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * feat(addons): carry host policy actions to owner Keep host policy reads and updates on the authenticated owner. The owner checks current administrator authority before updates and preserves CAS conflicts and durable operation receipts across forwarding. Provide read-only snapshots for trusted hosts over pinned mTLS. Missing owner services never fall back to frontend policy. Application startup, public settings routes and runtime consumers remain separate work. Verify typed actions with local and real mTLS forwarding tests, including revoked actors, strict payloads, receipt replay and unavailable stores. Assisted-by: Codex:gpt-5 Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * fix(addons): keep policy provenance private Return a bounded policy projection from gateway reads, updates and replay. Trusted host consumers use the same projection without migration metadata. The private owner store retains provenance for migration records. Cover local and pinned-carrier output, durable provenance and typed errors. Assisted-by: Codex:gpt-5 Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * feat(addons): initialize owner host settings Open durable host settings only after the owner acquires its storage guard. Keep frontends forwarding without importing their local configuration. Preserve automatic migration for the sole combined host configuration. Require explicit owner-local selection for non-default dedicated policy. Close the store once on cancellation, shutdown or startup failure. Cover migration, restrictive defaults, restart revisions, corruption, duplicate owners and frontend isolation in the native bootstrap test. Document the owner-only migration option and storage constraints. Assisted-by: OpenAI:ChatGPT Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * fix(addons): close settings before the owner guard Startup failures released the owner guard before closing host settings. Use one deferred cleanup to preserve the close order on every failure. Test real runtime and gateway failures, shutdown, and cancellation. Check once-only closure and reacquire both locks after cleanup. Assisted-by: Codex:gpt-5 Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * fix(addons): enforce owner egress policy in Studio Read owner policy for Studio defaults, views, cards and enforcement. Frontend configuration must not override policy or mask an owner outage. Propagate context and policy errors instead of reusing a cached allow. Preserve local settings behavior when add-ons are disabled. Cover existing consumers through independent pinned mTLS carriers. Verify live tightening, pending-card outages and restart persistence. Document the read semantics and the proxy verdict-cache boundary. Assisted-by: OpenAI:gpt-5 Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * fix(addons): read provider pins from owner policy Studio reads current owner pins for tools and skills instead of frontend configuration when add-ons are enabled. Policy failures stop selection rather than silently choosing built-ins. Disabled add-ons retain local configuration behavior. Cover owner updates and outages, and document the read contract. Assisted-by: Codex:gpt-5 Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * fix(studio): abort turns on mandatory policy errors Sessions must not treat owner policy failures as optional tool outages. Stop priming before harness startup and abort turns before model sends. Preserve wrapped policy causes and existing optional connector behavior. Cover sent turns, priming failures, and optional connector failures. Document the failure contract without changing fallback or cache rules. Assisted-by: Codex:gpt-5 Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * fix(addons): use owner policy for auto-upgrades Read fresh owner policy before each automatic upgrade pass. Skip the pass when that policy is unavailable instead of trusting local settings. Wire a real owner policy store into the transportless manager fixture. Keep assertions for stale frontend settings and owner policy changes. Assisted-by: Codex:gpt-5 Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * fix(addons): guard legacy settings writes Reject migrated owner policy fields before applying any local settings. Omit those fields from legacy reads while add-ons are enabled, and keep unrelated local settings available without an owner request. Cover presence, case folding, duplicate keys and atomic rejection. Document the legacy contract and unchanged disabled behavior. Assisted-by: Codex:gpt-5 Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * test(addons): use the existing Ginkgo suites Register settings authority regressions with the existing suites to comply with the Ginkgo/Gomega test policy. Preserve the conflict, compatibility, owner outage, and upgrade assertions without adding another RunSpecs bootstrap. Assisted-by: Codex:gpt-5 Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * feat(egress): bound wildcard host policy grants Support descendant and public-any patterns through the shared parser. Keep wildcard grants inside the administrator ceiling and public guard. Preserve the legacy exact-host exception under the registries ceiling. Keep policy strings lossless for existing receipts and stored exact hosts. Test canonical matching, private exclusions, ports, and policy replay. Proxy connection authorization and cache changes remain separate. Assisted-by: Codex:gpt-5 Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * fix(egress): authorize each new connection Remove cached allows so policy tightening and owner outages block new connections immediately. Keep existing DNS, private-address and SNI guards. Socket tests cover HTTP keep-alive, CONNECT and consumed grants. Established streams may finish under their original authorization. Expect the canonical runsc path in the gVisor test, matching production. This preserves the path contract when the test temp directory is a symlink. A short alias avoids Unix socket path limits in deep worktrees. Verify with go test -race -count=1 -timeout=180s ./addons/sandbox/... using an offline environment and a short alias to the worktree temp folder. Assisted-by: Codex:gpt-5 Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * fix(addons): save network policy through the owner Use owner revisions for host controls instead of frontend runtime settings. Keep drafts across conflicts and outages, and replay uncertain writes with the same operation identity. Read authoritative policy after each save. Accept bounded host wildcards while keeping extra registries exact. Keep typed private-access confirmation out of owner policy patches. Add rendered form and client tests, and document reconciliation behavior. Assisted-by: OpenAI: GPT-5 Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * fix(addons): fail closed on owner policy errors Reject partial, mistyped, unknown and oversized owner snapshots before publishing them to the form. Match the complete Go policy JSON shape. Keep owner availability errors and uncertain operation identities when Discard resets the local draft. Only reconciliation can reopen saving. Tests reproduce both review failures and cover immutable retries, malformed reloads and the rendered discard-then-edit regression. Assisted-by: Codex:gpt-5 Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * fix(addons): finish owner settings controls Route skill switches, provider choices and upgrade controls through owner policy. Preserve per-key changes when another frontend updates the policy. Keep local tool execution controls on their existing settings path. Extend the existing state checklist and add-on acceptance guidance so new controls declare their authority and test both deployment topologies. Assisted-by: nib:gpt-6.1-sol Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * fix(studio): check policy errors in Tables skill tests Adapt the Tables test added on master to the error-aware skillTurn API. Assert that normal skill selection succeeds instead of discarding errors. Verified with make test-distributed-fakebus and scoped golangci-lint. Assisted-by: nib:gpt-6.1-sol Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * fix(addons): document guarded settings IO boundaries Explain the narrow gosec exceptions for the authenticated administrator settings response and fixed owner-root files. Keep symlink, mode and type checks intact; no request-supplied paths reach this store. Explicitly ignore best-effort temporary-file and directory cleanup errors; file and directory sync failures still determine durability. Verified owner-store and handler race tests and scoped golangci-lint. Assisted-by: nib:gpt-6.1-sol Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * test(addons): run carrier policy cases in Ginkgo Register projection, forwarding, validation and CAS cases with the existing gateway suite. Check cleanup errors in test fixtures and explicitly ignore response-body cleanup after decoding in the internal read client. Verified gateway/application authority race tests and scoped lint. Assisted-by: nib:gpt-6.1-sol Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * test(addons): inventory owner policy gateway cases Require all four owner-policy cases in both the per-report and aggregate gateway and artifact acceptance inventories. Keep exact matching intact. Verified real test reports with the existing strict Ginkgo report validator: 215 gateway cases and 252 artifact cases passed, without skipped cases. The full browser readiness probe remains a separate CI gate. Assisted-by: nib:gpt-6.1-sol Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * test(ui): scope library settings apply action Select the exact Apply button in the runtime settings pending bar. The owner upgrade control has a separate Apply upgrade setting button, so an unscoped substring locator is ambiguous. Verified the failing Playwright case against the real UI test server and Google Chrome, with isolated server and browser temporary paths. Assisted-by: nib:gpt-6.1-sol Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * test(egress): update route and invalid-host fixtures Use the context-aware egress callback in route tests. Keep the last-valid list regression meaningful by using a partial-label wildcard, since a whole-label descendant pattern is now valid input. Verified focused application and route race tests and scoped lint. Assisted-by: nib:gpt-6.1-sol Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * test(settings): reject partial-label wildcard fixture Keep the invalid-input regression while accepting the newly supported whole-label descendant wildcard. Use api.*.example.com as invalid input. Reproduced the full endpoint suite failure, then verified all 616 endpoint specs pass and scoped lint is clean. Assisted-by: nib:gpt-6.1-sol Signed-off-by: Ettore Di Giacinto <mudler@localai.io> --------- Signed-off-by: Ettore Di Giacinto <mudler@localai.io> Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
423 lines
27 KiB
Markdown
423 lines
27 KiB
Markdown
+++
|
|
title = "Focused addon testing"
|
|
toc = true
|
|
description = "Prepare and verify the toolchain for focused addon tests."
|
|
+++
|
|
|
|
The addon verification scripts separate explicit dependency setup from offline checks.
|
|
Run all commands from the repository root. Only the `prerequisites` suite is currently available.
|
|
Unknown suites, empty selections, skipped required tests, and failed commands return a nonzero status.
|
|
|
|
## Required tools
|
|
|
|
Provide these tools before running setup:
|
|
|
|
- Go 1.26.x, with `GOTOOLCHAIN=local`. Other minor versions are rejected.
|
|
- Node 22.12 or later within Node 22, with npm. The validated setup uses Node 22.22.0.
|
|
- `protoc` 26.1. The general Makefile downloader uses a different version.
|
|
- A native C compiler (`cc`, or the executable specified by `CC`) with `CGO_ENABLED=1`.
|
|
- Python 3, POSIX shell tools, and the runtime libraries required by Chromium.
|
|
|
|
Setup and integration preparation require at least 20 GiB of free disk space before each major step.
|
|
They do not install system packages or fetch Go, Node, or `protoc` automatically.
|
|
Place locally provisioned executables in `.cache/addons/bin`, or put them on `PATH`.
|
|
|
|
For Linux x86-64, the validated tool archives have these SHA-256 checksums:
|
|
|
|
| Archive | Source | SHA-256 |
|
|
| --- | --- | --- |
|
|
| `node-v22.22.0-linux-x64.tar.xz` | [Node 22.22.0 distribution](https://nodejs.org/dist/v22.22.0/) | `9aa8e9d2298ab68c600bd6fb86a6c13bce11a4eca1ba9b39d79fa021755d7c37` |
|
|
| `protoc-26.1-linux-x86_64.zip` | [Protocol Buffers 26.1 release](https://github.com/protocolbuffers/protobuf/releases/tag/v26.1) | `a7be2928c0454f132c599e25b79b7ad1b57663f2337d7f7e468a1d59b98ec1b0` |
|
|
|
|
Validate archive checksums before extraction. These archives do not apply to other architectures.
|
|
|
|
## Explicit setup
|
|
|
|
Setup downloads Go dependencies, installs the pinned protobuf plugins, and runs `npm ci` against the React lockfile.
|
|
It then installs Chromium through the local Playwright CLI, unless you supply an existing browser.
|
|
|
|
```sh
|
|
sh scripts/addons/prepare.sh --prepare
|
|
```
|
|
|
|
The protobuf plugin pins match the Makefile:
|
|
|
|
- `google.golang.org/protobuf/cmd/protoc-gen-go@v1.34.2`
|
|
- `google.golang.org/grpc/cmd/protoc-gen-go-grpc@1958fcbe2ca8bd93af633f11e97d44e567e945af`
|
|
|
|
Go resolves the second pin to module version `v1.4.0`.
|
|
The check validates each plugin's embedded module version and checksum, not its version label alone.
|
|
Go downloads use module checksum authentication. npm uses the lockfile's package integrity records.
|
|
|
|
The lockfile currently selects Playwright 1.58.2 and Chromium revision 1208 (Chrome 145.0.7632.6).
|
|
The installed Playwright package selects the browser revision; the scripts never invoke `npx`.
|
|
To provision only the browser after npm setup, run:
|
|
|
|
```sh
|
|
export PATH="$PWD/.cache/addons/bin:$PATH"
|
|
export PLAYWRIGHT_BROWSERS_PATH="$PWD/.cache/addons/browsers"
|
|
core/http/react-ui/node_modules/.bin/playwright install chromium
|
|
```
|
|
|
|
Keep `PLAYWRIGHT_BROWSERS_PATH` set to this worktree-local path for manual installation.
|
|
The preparation and verification scripts set it automatically.
|
|
A browser installed in Playwright's default user cache does not satisfy this setup.
|
|
|
|
Alternatively, set `PLAYWRIGHT_CHROMIUM_PATH` to an absolute executable path before setup and verification.
|
|
Setup then skips the browser download. Verification still requires a successful browser launch and rendered UI.
|
|
Neither option installs OS libraries. If launch fails, inspect the reported library names and the browser executable with `ldd` on Linux.
|
|
Provide a compatible runtime environment or report the missing libraries as a blocker; do not skip the browser probe.
|
|
|
|
## Prepare generated inputs
|
|
|
|
After explicit setup, generate only the inputs needed by the focused tests:
|
|
|
|
```sh
|
|
sh scripts/addons/prepare.sh --integration
|
|
```
|
|
|
|
This command generates `pkg/grpc/proto`, builds the React distribution, and builds two auth-enabled test binaries:
|
|
|
|
- `tests/e2e/mock-backend/mock-backend`
|
|
- `tests/e2e-ui/ui-test-server`
|
|
|
|
It does not build inference backends or run a full repository build.
|
|
It disables Go dependency downloads and requires the populated setup cache.
|
|
Repeat this step when the proto, UI, or test-server sources change.
|
|
|
|
## Check and verify
|
|
|
|
Inspect the toolchain, installed dependencies, and generated inputs without downloading or rebuilding them:
|
|
|
|
```sh
|
|
sh scripts/addons/prepare.sh --check
|
|
```
|
|
|
|
This read-only dependency check is not a browser or SQLite runtime test.
|
|
Run the complete readiness gate separately:
|
|
|
|
```sh
|
|
sh scripts/addons/verify.sh prerequisites
|
|
```
|
|
|
|
Verification performs these checks:
|
|
|
|
1. Run isolated shell fixtures for missing tools, incompatible versions, failed children, invalid reports, and empty or skipped selections.
|
|
2. Inspect the real toolchain and generated inputs.
|
|
3. Compile and execute a SQLite transaction with CGO, Ginkgo v2, and Gomega.
|
|
4. Start the real UI test server with the mock backend on an isolated loopback port.
|
|
5. Wait for server health, launch Chromium, and require a rendered React root.
|
|
|
|
The SQLite probe uses `go test -count=1 -tags auth` and a machine-readable Ginkgo report.
|
|
Verification rejects missing, empty, failed, skipped, or unexpected test cases.
|
|
It records child commands, output, and status in `.cache/addons/logs/verify-*.jsonl`.
|
|
Ginkgo reports use separate unique files in the same directory.
|
|
Server processes stop after the probe, including when the browser check fails.
|
|
|
|
Checks and verification never install dependencies or browsers.
|
|
They set `GOTOOLCHAIN=local`, `GOPROXY=off`, `GOSUMDB=off`, and `GOFLAGS=-mod=readonly` for verification builds.
|
|
The npm dependency check uses `--offline`.
|
|
Missing cached modules or browser files are failures, not permission to download them.
|
|
|
|
## Cache layout and test inventory
|
|
|
|
The scripts keep caches and transient results under `.cache/addons`:
|
|
|
|
| Path | Purpose |
|
|
| --- | --- |
|
|
| `bin` | Local tools and protobuf plugins; prepended to `PATH` |
|
|
| `go`, `go/pkg/mod` | `GOPATH` and `GOMODCACHE` |
|
|
| `go-build` | `GOCACHE` |
|
|
| `npm` | npm cache |
|
|
| `browsers` | Playwright browser installations |
|
|
| `tmp` | Setup, fixture, and verification scratch data |
|
|
| `logs` | Command results and Ginkgo reports |
|
|
| `downloads` | Optional archives from explicit local provisioning |
|
|
|
|
Do not commit caches, generated distributions, or test binaries.
|
|
Keep the command and required-case inventory in `scripts/addons/suites.json` synchronized with the runner.
|
|
Add future suites only when their actual tests and commands exist.
|
|
A successful prerequisite gate demonstrates readiness, not acceptance of addon functionality that has no tests yet.
|
|
|
|
## Gateway verification
|
|
|
|
Run `sh scripts/addons/verify.sh gateway` for the owner carrier and real-auth HTTP suites.
|
|
The inventory lists each report separately and requires their exact combined case count.
|
|
Missing reports, skipped cases, empty reports, and unsuccessful child commands fail the gate.
|
|
The tests use local certificate authorities, pinned mTLS peers, two receiving frontends,
|
|
and a real SQLite identity database. No external identity service or addon port is exposed.
|
|
|
|
The disconnect fixture consumes the POST envelope before waiting for cancellation.
|
|
Leaving that body unread prevents the HTTP/1 server from observing disconnect while
|
|
its handler waits, which can strand test-server cleanup. Production owner handlers decode
|
|
the bounded envelope before starting a relay.
|
|
|
|
## Data recovery suite
|
|
|
|
```sh
|
|
sh scripts/addons/verify.sh recovery
|
|
```
|
|
|
|
The `recovery` suite checks backup, upgrade with a migration, restore and purge
|
|
of add-on data. `scripts/addons/recovery.sh` runs, in this order: the node unit
|
|
tests of the panel rules, the typed client and the locale keys; the Go specs of
|
|
`core/services/addons/recovery` (every journal phase of an upgrade and of a
|
|
restore, with failures injected through test-only seams, cancellation, a
|
|
corrupt backup, too little storage, retention, grant resurrection and purge),
|
|
the specs of the policy hold and restore filter, the specs of the Tables format
|
|
migration, and `tests/addons/recovery`, which runs the real owner, real
|
|
accounts and the real Tables executable over HTTP; and a Playwright spec against
|
|
a UI test server. Set `RECOVERY_SHOTS` to a directory to keep the screenshots of
|
|
the Playwright run. The suite is part of the foundation gate. Run
|
|
`sh scripts/addons/prepare.sh --integration` first: the runner refuses a
|
|
server older than the recovery code or a UI build older than the panel.
|
|
|
|
## Foundation acceptance gate
|
|
|
|
The foundation gate is the end-to-end check of everything below the first
|
|
add-on: the owner, the carrier, the lifecycle, the policy, the proposals, the
|
|
file results, the Apps surface and the UI asset route. It is one integration
|
|
fixture, not a set of fakes at the seams.
|
|
|
|
```sh
|
|
sh scripts/addons/verify.sh foundation-acceptance # the end-to-end suite
|
|
sh scripts/addons/foundation_gate.sh # every foundation suite
|
|
```
|
|
|
|
Run `sh scripts/addons/prepare.sh --integration` first. The gate builds the
|
|
server, the protocol fixture and the cluster command itself, so it never tests a
|
|
stale binary.
|
|
|
|
### The fixture
|
|
|
|
`tests/addons/foundation/cluster` starts three server processes of
|
|
`tests/e2e-ui`: the owner and two frontends (`front-a`, `front-b`). Each has its
|
|
own listener, data directory and carrier identity. They share one account
|
|
database, and they speak the real pinned mTLS carrier. The frontends open no
|
|
add-on storage and start no process. The add-on process is the protocol fixture
|
|
(`tests/addons/fixture/cmd`), packaged three ways from a catalog served on
|
|
loopback after the host was built:
|
|
|
|
| Package | Declares |
|
|
| --- | --- |
|
|
| `found-ui` | a service, the tools `echo` and `export`, and a UI module |
|
|
| `found-tool` | a service and the same tools, no UI |
|
|
| `found-svc` | a service only: no tool and no UI |
|
|
|
|
The fixture add-on reads `fixture.json` from its package. A mode changes how it
|
|
fails: `exit-start`, `never-ready`, `lose-reply` (commit, then drop the reply)
|
|
and `orphan` (ignore the closing of the startup channel). Its commits and every
|
|
apply request are written to a durable log, so a spec counts side effects across
|
|
restarts. Specs use real API keys and real browser sessions; the only route that
|
|
is not product code is the owner's loopback control route that registers a
|
|
resource, because the fixture has no add-on that creates one.
|
|
|
|
### What the gate proves
|
|
|
|
| Case group | Proves |
|
|
| --- | --- |
|
|
| install, activate, alternate | An install and an enable through a frontend reach the owner, start one process per add-on under the owner only, and reads alternate through the owner and both frontends |
|
|
| host unchanged | The host binary and the shipped UI build have the same digest after the whole lifecycle |
|
|
| actor propagation | The add-on sees the actor of the credential; forged actor and authority headers change nothing, and a forged administrator cannot reach lifecycle |
|
|
| JSON, progress, files, assets | JSON and file relays and lifecycle progress work through a non-owner frontend, a private file downloads through another frontend, and UI assets are served by digest, immutable and only for declared files |
|
|
| denials | A missing feature, a stranger, a stale generation and an undeclared tool give the same typed denial on every node |
|
|
| exactly once | A confirmed change reaches the add-on once, even confirmed again through another frontend; a lost reply is `outcome_unknown` with the original operation ID and is settled by receipt lookup with no second apply |
|
|
| owner down | Every frontend answers 503 with the same body, starts nothing and creates no storage; a restart brings back the last good package and the retained data |
|
|
| no duplicate owner | A second owner on the same storage does not keep running, and each add-on keeps one process |
|
|
| orphans | A process that outlived its owner is stopped before the next one starts |
|
|
| failed upgrade and enable | A new generation that cannot start leaves the last good one running; killing the owner in the middle of an upgrade or an enable keeps the last good code, the retained data and a usable lifecycle |
|
|
| disable, remove, reinstall | Data and grants outlive the removal, and a stale generation and its assets are denied after an upgrade |
|
|
| sessions and keys | Ending a session or revoking a key on one frontend refuses it on every node |
|
|
| browser | A member sees the add-on list only and the server refuses every management request; Open works through a non-owner frontend; a proxy path prefix works for deep links; a session ended on one frontend loses the assets on the others |
|
|
| service layer | An upgrade that cannot start keeps the last good generation running and the data; a request canceled during the start puts the last good generation back, keeps the operation open and the same operation ID finishes it; one broken add-on does not stop the owner |
|
|
| regression | The existing model and backend gallery specs (`core/gallery`), the gallery operation specs (`core/services/galleryop`) and the process adapter specs (`pkg/model`) pass |
|
|
|
|
The regression packages are the narrowest ones that run offline in under two
|
|
minutes each and that cover the code the add-on lifecycle sits beside. The
|
|
`core/gallery` model install specs run with `FIXTURES=tests/fixtures`. The only
|
|
skips the gate accepts are the specs that state they need macOS.
|
|
|
|
One `pkg/model` spec, "includes the exit code and final stderr line for an
|
|
unexpected exit", replaces the process-wide logger to capture a warning. It is
|
|
a test of the upstream package and the gate does not change it. Run together
|
|
with the other specs of the package, goroutines of those specs can log while
|
|
the logger is swapped and fail it at random. The gate therefore runs the
|
|
package three times: a dry run that fixes the inventory, a run that skips that
|
|
one spec, and a run that focuses on it alone. The case passes only when the
|
|
dry run lists exactly one such spec, the second run passes every other spec
|
|
(or skips it for macOS) and the third run passes that spec once. A spec that is
|
|
not accounted for in exactly one of the runs fails the case. A real failure of
|
|
the spec still fails the gate, with no retry.
|
|
|
|
### Reading the result
|
|
|
|
`foundation_gate.sh` runs every suite in the gate set, in order, and prints the
|
|
inventory: the passed count and the skipped count of each suite, and the total.
|
|
It exits nonzero when a suite fails, reports a skipped case, or is missing from
|
|
the set. A suite that is added to `suites.json` must be named in the script.
|
|
The report of the parent review is this inventory plus the case list of
|
|
`foundation-acceptance` in `scripts/addons/suites.json`.
|
|
|
|
## Library sources suite
|
|
|
|
`sh scripts/addons/verify.sh library-sources` runs the library sources spec
|
|
(`core/http/react-ui/e2e/library-sources.spec.js`) in a real browser, against a
|
|
real UI test server that starts like a normal install: the default galleries,
|
|
the default library sources of the add-on, backend and model kinds, and add-ons
|
|
on with nothing installed. `playwright.library.config.js` starts that server
|
|
itself on a free port. The spec adds, turns off, removes and restores sources,
|
|
checks that each page lists only its own kind, and checks the "Use official
|
|
libraries" setting.
|
|
|
|
## Continuous integration
|
|
|
|
Each part of the add-on tests has a job that runs it, so a pull request cannot
|
|
skip them:
|
|
|
|
| What | Where |
|
|
| --- | --- |
|
|
| Go tests under `addons/` and the self-contained `tests/addons/actione2e` suite (real Sandbox and Office executables under the real supervisor) | `make test` and the coverage run, job `tests-linux` in `test.yml` |
|
|
| Every suite of `suites.json` except `packaging`, in five groups | `addons-tests.yml` |
|
|
| `packaging` | `addons-package.yml` |
|
|
| UI unit tests of every add-on, the host UI unit tests, eslint and the locale ratchet | `tests-ui-unit.yml` (`make test-ui-unit`) |
|
|
| Add-on pages in the browser against stubbed routes, and the shipped bundle | `tests-ui-e2e.yml` |
|
|
|
|
The suites of `addons-tests.yml` need the integration build, so each group runs
|
|
`prepare.sh --prepare` and `--integration` once, then `verify.sh` for each of
|
|
its suites. Run the same way locally with
|
|
`make test-addons-suites ADDON_SUITES='gallery-ui library-sources'`.
|
|
A suite added to `suites.json` must be named in a group of the workflow and in
|
|
`foundation_gate.sh` or `tables_gate.sh`.
|
|
|
|
## Packaging suite
|
|
|
|
`sh scripts/addons/verify.sh packaging` checks the first-party OCI package, its release
|
|
channels and its workflow. It runs `scripts/addons/package_test.sh`, which prints one
|
|
JSON line for each case. It needs no registry, no Docker and no network.
|
|
|
|
- Go specs of `pkg/addons/packaging`. They build real layouts and then rewrite one part
|
|
of them to prove each rule: missing entrypoint, missing declared UI, missing runtime
|
|
library, manifest or platform mismatch, unexpected layers or media types, digest
|
|
mismatch, invalid permissions, traversal paths, a bundled React runtime and a partial
|
|
platform set. They cover the event and channel table (empty, Unicode and long refs,
|
|
`latest`, `development`, semver-like names and two refs that normalize alike). They
|
|
publish to an in-process registry and check immutable tags, digest-identical retries,
|
|
stale promotions and a failed promotion that leaves aliases unchanged. They rewrite a
|
|
branch so that the commit an alias names is gone from the checkout, and check that
|
|
only a push to the channel's own ref takes it over, that `development` refuses, and
|
|
that other git errors fail. The workflow policy has one mutated copy of the workflow
|
|
for each rule, including the `validate` chain (`publish` must need `validate`,
|
|
`validate` must not use `continue-on-error`, `always()` or `failure()`).
|
|
- `tests/addons/packaging`. It builds the Tables executable, runs `package.sh`, publishes
|
|
to a loopback distribution, signs the platform image with the fixture authority under
|
|
the identity of the rendered catalog entry, installs it with the real installer and
|
|
starts the executable under the real supervisor. A signature of another workflow
|
|
identity, an owner policy that names the backend workflow and a wrong pinned digest
|
|
are each refused.
|
|
- Shell cases for `package.sh`, `channel.sh` and `addonpkg policy`, and static checks
|
|
that the packaging scripts never push, tag, sign or log in and never name a backend
|
|
registry.
|
|
|
|
The suite never signs, pushes or creates a tag against a real registry. The workflow
|
|
itself (registry login, keyless signing, the verification command) is covered by the
|
|
policy assertions and by review, not by a live run: running it needs the protected
|
|
environment of the repository.
|
|
|
|
## Tables acceptance
|
|
|
|
`sh scripts/addons/verify.sh tables-acceptance` is the end-to-end acceptance of the
|
|
first-party Tables add-on. `sh scripts/addons/tables_gate.sh` runs every `tables-*`
|
|
suite and then this one, in order, and prints the inventory like the foundation gate.
|
|
It fails for a suite that is skipped, that leaves a process behind or that is added to
|
|
`suites.json` and not named in the script.
|
|
|
|
What it runs, and what is real:
|
|
|
|
- **The cluster** is the foundation cluster of `tests/addons/foundation/cluster`: one
|
|
owner and two forwarding frontends, each a separate server process with its own
|
|
listener, one shared account database and the real pinned mTLS carrier. The accounts
|
|
are real: an administrator, the owner of a database, an editor, a viewer, a stranger
|
|
and an account without the add-ons feature, each with a stored session and an API key.
|
|
- **The package** is the real Tables. `scripts/addons/package.sh` builds the OCI layout
|
|
of this platform (twice, for two versions). The suite checks every layer against its
|
|
digest, joins the layers into the archive a catalog on loopback serves, and installs
|
|
it through a frontend as an administrator does. Nothing is pushed, tagged or signed,
|
|
and a receipt that says `publishable` is refused.
|
|
- **Calls** use the public HTTP routes only. There is no route into the add-on that a
|
|
user could not use, and no seam in the add-on: crashes are `SIGKILL`, a stopped
|
|
process is `SIGSTOP`, an owner outage is a killed owner process.
|
|
- **Reads of the database** by the test are read-only SQLite opens of the add-on's own
|
|
file, to count the receipts it committed.
|
|
|
|
The specs, by what they prove:
|
|
|
|
| Area | Specs |
|
|
|---|---|
|
|
| Roles | Owner, editor, viewer, administrator, stranger and an account without the feature, over the browser module route, the REST routes, the typed proposal route and the chat tool consumer, each through a different node. Blocked consumers (`/v1/messages`, `/v1/responses`, `/v1/mcp/chat/completions`) fail with the typed `addon_tools_unsupported`. |
|
|
| Credentials | A paused key, a withdrawn feature, a lowered role (and the proposals it held), a revoked grant and a disabled account are denied on the next call on every node. |
|
|
| Authority | Forged actor and forwarding headers, authority, role and approval fields, and every way to confirm a proposal without the proposer's own tab (a key with the right headers, the owner's tab, an administrator, a missing header, a tool-call marker, a wrong digest). |
|
|
| Consistency | Schema and data staleness from two tabs, replay of an operation ID from every node, a conflicting request under the same ID, a burst from three nodes with exactly one receipt for each acknowledged ID, and a revocation that races a confirmation. |
|
|
| Crash | The add-on process killed in the middle of a batch and the owner killed and restarted in the middle of a batch: every acknowledged change survives, no change is applied twice, and a worker repeats only the same ID. A stopped and killed process leaves an unresolved confirmation that blocks other writes until the owner role resolves it. |
|
|
| Lifecycle | Disable, enable, uninstall and install: stale tabs and proposals of the older generation do nothing, and data and grants remain. |
|
|
| Files | CSV through a frontend, downloads from every node by the requester only, a revoked viewer, a disabled add-on. |
|
|
| Outage | With the owner down, a JSON read, the JSON relay, progress, status, file status, file download, an asset, the apps list, a proposal, the pending list, a confirmation, an apply and a chat tool call all fail closed (503 with the same typed answer from both frontends), and the frontends run nothing and create no storage. |
|
|
| Recovery | Backup, verify and restore by the administrator's tab; a corrupt backup, a running add-on, a wrong confirmation, an unknown backup. |
|
|
| Upgrade | An explicit check, a redacted status for a member, a key refused, a stale or changed pin refused, one pinned upgrade, the data and grants kept, a backup made first, and a replay of the same operation from another node. |
|
|
| Browser | Approval cards from chat on one frontend and the result on the other, prefix deep links, keyboard, 400 pixels, both themes, the unavailable page while the owner is down and the reload after it is back, and nothing left after logout. |
|
|
|
|
### The evidence manifest
|
|
|
|
Each run writes `.cache/addons/tables-acceptance-evidence.json`: the commands the run
|
|
executed, the case inventory with the state of every case, the native platform
|
|
(operating system and architecture), the digests of both packaged versions (the
|
|
archive the catalog serves and the image of the OCI layout), a statement that nothing
|
|
was published, and, for a case that failed, the path of the retained trace (the logs of
|
|
the three servers). A Go test (`TestEvidenceManifest`) checks it against the inventory
|
|
of `scripts/addons/suites.json`: the platform is native, the digests are well formed and
|
|
distinct, every case passed, and a failed case has a trace that exists.
|
|
|
|
### Faults the real executable cannot show on its own
|
|
|
|
Three cases need a fault that the product executable does not have, and the product
|
|
executable gets no flag or variable for them. The suite builds a second, test-only
|
|
executable from `addons/tables/cmd/tables-faults`, packages it with the same
|
|
`scripts/addons/package.sh` and installs it on one cluster. It serves exactly what the
|
|
product serves. A spec arms a fault by creating a file in the `fault` directory of the
|
|
add-on's data:
|
|
|
|
- `crash-after-commit` ends the process after the next change committed and before the
|
|
answer is sent. The spec checks that the host reports the original operation ID as
|
|
unknown from the frontend, that the receipt lookup finds it committed, and that the same
|
|
operation again returns the same receipt: one row and one receipt in total.
|
|
- `hold-output` keeps a stream of the add-on from being served, so a transfer stays in
|
|
flight. The spec disables the add-on and checks that the artifact ends without being
|
|
downloadable on any node, that no byte stays on disk and that a new export works.
|
|
|
|
The expiry spec needs a short artifact lifetime. The fixture's cluster configuration has
|
|
an `artifactExpiryMillis` field, which the test launcher passes to the owner as the
|
|
programmatic `AddonConfig.ArtifactExpiry` option. No flag sets it and the default stays
|
|
one hour. The spec checks that every node gives the same denial for the expired file
|
|
and that the database and the backups are untouched.
|
|
|
|
### What it does not prove
|
|
|
|
- **Other platforms.** The evidence is of the platform it ran on. Advertising another
|
|
platform needs a native run on that platform; the platform matrix keeps it disabled.
|
|
- **The free space seam** is a storage bound that needs a seam the real add-on does not
|
|
have. The `recovery` suite covers it with an in-process seam. The expiry and the
|
|
withdrawal of an artifact are covered here (see below).
|
|
- **A needs-recovery upgrade** needs a data format change that fails to migrate. The
|
|
`recovery` suite covers it; this suite covers the successful upgrade and the restore.
|
|
- **Which interleaving a race takes.** The race and the crash specs accept whatever
|
|
order happens and check the invariants. The committed-and-lost-reply case is not left
|
|
to chance: see the fault specs below.
|
|
- **A refused request is a typed `400`.** A request that the add-on refuses as invalid
|
|
(an unknown table, an unknown field) is `400` `invalid_request` on every route: the
|
|
runtime client types the add-on's HTTP 400 and 422 as a refusal, and the host answers
|
|
it the same way from the apply, read, proposal, chat and artifact paths, and from the
|
|
owner and every frontend. The authorization specs pin the exact status.
|
|
|
|
### Settings authority in both topologies
|
|
|
|
For every add-on settings surface, run combined-mode and owner-with-two-frontends cases. Save through one frontend and verify the other frontend and the real consumer agree. Repeat a restrictive update, restart the owner, and interrupt owner access. Assert explicit errors rather than frontend-local settings or built-in fallback. Exercise CAS conflicts, per-key provider deletion, uncertain retries, and preservation of unrelated fields. Network policy tests must use new HTTP and CONNECT sockets after revocation; established streams may finish. Add rendered-form tests for the actual dispatch payload and disabled controls during failed reconciliation. Follow the existing distributed-state checklist before introducing a new settings path.
|