| """Tenant definitions β one entry per client of the platform. |
| |
| Royal Imports is the first tenant. Everything that was HARD-CODED in core/odoo.py (teams 5/6, the |
| GIFTWARE house account, the sale/done statuses) moves here as per-client CONFIG. A second client is |
| just another Tenant with its own connectors + config β the skills and UI don't change. |
| |
| In production this comes from a tenants store (DB / HF dataset), not a literal; kept literal here so |
| the pivot is legible and runnable today. |
| """ |
| from __future__ import annotations |
| from harness.base import Tenant |
| from harness.canonical import Capability |
| from harness.connectors.odoo import OdooConnector |
|
|
|
|
| def royal_imports(doc_mode: str = 'order') -> Tenant: |
| odoo = OdooConnector(doc_mode=doc_mode) |
| return Tenant( |
| key='royal-imports', name='Royal Imports', |
| sources={odoo.key: odoo}, |
| config={ |
| 'business_units': {'Fisch': 5, 'Royal': 6}, |
| 'confirmed_statuses': ['sale', 'done'], |
| 'excluded_customer_ids': OdooConnector.excluded_customer_ids(), |
| 'channels': {'Amazon': 'GIFTWARE DEALS'}, |
| 'branding': {'navy': '#1F4E78', 'gold': '#C8A24B', 'name': 'Royal Imports'}, |
| }) |
|
|
|
|
| class _EmptyConnector: |
| """A source that answers every canonical query with NOTHING β the X8 isolation fixture. |
| |
| Deliberately NOT a mock library object and NOT a subclass shortcut around `aggregate`: the |
| isolation proof has to run against the SAME code path a real tenant uses, so this answers the |
| real interface and returns real (empty) results. A test double that skipped the interface |
| would prove the double is isolated, not that the runtime is. |
| |
| An EMPTY pool is the strongest possible fixture for the assertion that matters: tenant B's |
| `/customers` must return ZERO rows. Any row at all in that response came from somewhere it |
| should not have, and there is no ambiguity about whose it is. |
| """ |
| key = 'empty' |
| name = 'Empty (QA)' |
|
|
| def capabilities(self) -> set: |
| return set() |
|
|
| def aggregate(self, q) -> list[dict]: |
| return [] |
|
|
| def records(self, q) -> list[dict]: |
| return [] |
|
|
| def distinct_count(self, q, fieldname: str) -> int: |
| return 0 |
|
|
| def supports(self, cap: Capability) -> bool: |
| return False |
|
|
| def entities(self) -> set: |
| return set() |
|
|
|
|
| def qa_tenant_b() -> Tenant: |
| """Tenant 'qa-b' β the SECOND tenant the isolation proof needs, with no Odoo behind it. |
| |
| D-2's assertion is "tenant B cannot reach tenant A", and that cannot be tested with one |
| tenant configured. This is the minimum second tenant: its own slug (so its own store |
| namespace and its own DuckDB path), no credentials, and a connector that returns nothing. |
| |
| β NOT registered by default. `harness.runtime` only admits it when `AIOS_ENABLE_QA_TENANT=1`, |
| so a production deployment has exactly one tenant and an attacker cannot mint a session |
| against a fixture. (X8 says "the registry gains qa-b"; gating it is an additive safety |
| amendment, recorded in the S1 mailbox 2026-07-30 β the isolation MECHANISM under test, the |
| store namespace and the cookie's tenant claim, is identical either way.) |
| """ |
| empty = _EmptyConnector() |
| return Tenant( |
| key='qa-b', name='QA Tenant B', |
| sources={empty.key: empty}, |
| config={ |
| 'business_units': {}, |
| 'confirmed_statuses': [], |
| 'excluded_customer_ids': [], |
| 'channels': {}, |
| 'branding': {'name': 'QA Tenant B'}, |
| }) |
|
|
|
|
| |
| |
| |
| |
| |
| |
| |
|
|