Initial Commit
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
<!-- markdownlint-disable MD024 -->
|
||||
|
||||
# Capability Spec Delta: api-tenant-provider-memory (api-tenant-provider-memory)
|
||||
|
||||
**Package**: `@suaveplan/api-tenant-provider-memory`
|
||||
**Status**: PROPOSED
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Enumerate tenants from in-memory state
|
||||
|
||||
The package SHALL implement `TenantProvider` with `list()` (sync snapshot), `listAsync()` (async iterable), and `get(tenantId)`. Initial state comes from `options.tenants`; runtime mutations are gated by `options.mutable`.
|
||||
|
||||
#### Scenario: list returns the initial set
|
||||
|
||||
- **WHEN** the provider is constructed with `tenants: [A, B, C]`
|
||||
- **THEN** `list()` returns an array of length 3 containing A, B, C in insertion order
|
||||
|
||||
#### Scenario: async iterator yields every tenant exactly once
|
||||
|
||||
- **WHEN** `for await (const t of provider.listAsync())` runs against an initial set of 3 tenants
|
||||
- **THEN** exactly 3 tenants are yielded AND each ID appears exactly once
|
||||
|
||||
#### Scenario: get returns undefined for unknown tenant
|
||||
|
||||
- **WHEN** `get("unknown")` is called
|
||||
- **THEN** the result is `undefined` (no throw)
|
||||
|
||||
**Verified by:** `src/factory/create-memory-tenant-provider.test.ts::"list returns initial set in insertion order"` AND `src/factory/create-memory-tenant-provider.test.ts::"listAsync yields every tenant exactly once"` AND `src/factory/create-memory-tenant-provider.test.ts::"get returns undefined for unknown tenant id"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: Mutation API gated by mutable flag
|
||||
|
||||
When `options.mutable: true`, the provider SHALL expose `add(tenant)`, `remove(tenantId)`, `update(tenantId, patch)`. When `mutable: false`, those methods MUST throw `TENANT_PROVIDER_MEMORY_IMMUTABLE`.
|
||||
|
||||
#### Scenario: add rejects duplicate id
|
||||
|
||||
- **WHEN** `add({ id: "acme", ... })` is called twice with the same id
|
||||
- **THEN** the second call throws `TENANT_PROVIDER_MEMORY_DUPLICATE_ID`
|
||||
|
||||
#### Scenario: remove of unknown tenant throws
|
||||
|
||||
- **WHEN** `remove("not-there")` is called
|
||||
- **THEN** the call throws `TENANT_PROVIDER_MEMORY_NOT_FOUND`
|
||||
|
||||
#### Scenario: mutation blocked when immutable
|
||||
|
||||
- **WHEN** the provider is constructed with `mutable: false` AND `add(...)` is called
|
||||
- **THEN** the call throws `TENANT_PROVIDER_MEMORY_IMMUTABLE` AND state remains unchanged
|
||||
|
||||
**Verified by:** `src/factory/create-memory-tenant-provider.test.ts::"add rejects duplicate id"` AND `src/factory/create-memory-tenant-provider.test.ts::"remove of unknown tenant throws"` AND `src/factory/create-memory-tenant-provider.test.ts::"mutation blocked when immutable"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: Direct and worker-proxy surfaces both pass conformance
|
||||
|
||||
The proxy surface SHALL support `listAsync()` across the `MessagePort` (chunked yield).
|
||||
|
||||
#### Scenario: direct conforms
|
||||
|
||||
- **WHEN** `runTenantProviderConformance("memory-direct", () => createMemoryTenantProvider(...))` runs
|
||||
- **THEN** every invariant passes
|
||||
|
||||
#### Scenario: worker-proxy conforms with async iteration
|
||||
|
||||
- **WHEN** `runTenantProviderConformance("memory-worker-proxy", ...)` runs over a real `MessageChannel` AND `listAsync()` is iterated to completion against a 1_000-tenant fixture
|
||||
- **THEN** every invariant passes AND every tenant id is yielded exactly once on the proxy side
|
||||
|
||||
**Verified by:** `src/factory/create-memory-tenant-provider.spec-conformance.test.ts::"memory-direct conforms to TenantProvider contract"` AND `src/proxy/create-memory-tenant-provider-worker-proxy.spec-conformance.test.ts::"memory-worker-proxy conforms with async iteration across MessagePort"`
|
||||
|
||||
<!-- markdownlint-enable MD024 -->
|
||||
Reference in New Issue
Block a user