Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions docs/schema-codegen.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,16 @@ schema uses (enums, closed/open objects, `$ref`, `anyOf [T, null]`, arrays,
scalars); extending the wire model with a new construct may require teaching the
emitter about it.

The conformance check covers both SDK surfaces: `wire-conformance.test.ts` pins
the one-shot public types in `sdk/src/types.ts`, and
`wire-conformance-state-aware.test.ts` pins the state-aware lifecycle types in
`sdk/src/state-aware-types.ts` (the `Phase` and sizing-profile enums, the Entra
user bundle, and the per-phase `IsolationSessionPhase` field set) against the
same generated wire defs. Both share the assertion helpers in
`sdk/tests/unit/conformance-helpers.ts` and check drift in both directions
(public→wire and wire→public) so a new wire field the SDK forgets to expose also
fails the build.

### Why a hand-written emitter (alternatives considered)

The generated `wire.ts` is a **drift oracle, not the public API**. The public
Expand Down
2 changes: 1 addition & 1 deletion sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"watch": "tsc --watch",
"clean": "rimraf dist",
"test": "npm run test:unit",
"test:unit": "npm run build:test-unit && node --test dist-tests/tests/unit/sandbox.test.js dist-tests/tests/unit/policy.test.js dist-tests/tests/unit/logger.test.js dist-tests/tests/unit/errors.test.js dist-tests/tests/unit/state-aware-types.test.js dist-tests/tests/unit/state-aware.test.js dist-tests/tests/unit/platform.test.js dist-tests/tests/unit/wire-conformance.test.js",
"test:unit": "npm run build:test-unit && node --test dist-tests/tests/unit/sandbox.test.js dist-tests/tests/unit/policy.test.js dist-tests/tests/unit/logger.test.js dist-tests/tests/unit/errors.test.js dist-tests/tests/unit/state-aware-types.test.js dist-tests/tests/unit/state-aware.test.js dist-tests/tests/unit/platform.test.js dist-tests/tests/unit/wire-conformance.test.js dist-tests/tests/unit/wire-conformance-state-aware.test.js",
"test:integration": "cd tests/integration && npm install && npm run build && npm test",
"prepublishOnly": "npm run build"
},
Expand Down
54 changes: 54 additions & 0 deletions sdk/tests/unit/conformance-helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

// Shared compile-time type-assertion helpers for the wire-conformance oracles
// (`wire-conformance.test.ts` for the one-shot surface and
// `wire-conformance-state-aware.test.ts` for the state-aware surface). These are
// type-only; the emitted `.js` is empty, so this module is never run by the test
// runner — it exists to keep the two oracles' helpers identical.

/** Compiles only when `T` is exactly `true`. */
export type AssertTrue<T extends true> = T;

/** Drop the `[k: string]: unknown` index signature the emitter writes on open objects. */
export type StripIndex<T> = { [K in keyof T as string extends K ? never : K]: T[K] };

/**
* Recursively drop index signatures (open objects nest: e.g. `experimental.wslc`
* and the `IsolationSession*` objects), so structural assignment is not tripped
* by an emitted `[k: string]: unknown` at any depth. Modifiers (`?`) are
* preserved because the mapped type is homomorphic over `keyof T`.
*/
export type DeepStripIndex<T> = T extends (infer E)[]
? DeepStripIndex<E>[]
: T extends object
? { [K in keyof T as string extends K ? never : K]: DeepStripIndex<T[K]> }
: T;

/** `true` iff every value of `A` is assignable to `B` (index signatures stripped, recursively). */
export type Assignable<A, B> = [A] extends [DeepStripIndex<B>] ? true : false;

/**
* Keys present on the public type but absent from the wire type. `StripIndex`
* drops the `[k: string]: unknown` on open generated objects; without it
* `keyof Wire` would include `string` and `Exclude` would collapse to `never`,
* making public-only key checks vacuous for open objects.
*/
export type OnlyInPublic<Pub, Wire> = Exclude<keyof Pub, keyof StripIndex<Wire>>;

/**
* Keys present on the wire type but absent from the public type. Because every
* generated wire field is optional, `Public extends Wire` stays true when the
* SDK simply forgets a new wire field, so the value/`OnlyInPublic` checks alone
* do NOT catch a wire-only ADDITION. This closes that direction. `StripIndex`
* drops the `[k: string]: unknown` so the open objects don't make `keyof`
* collapse to `string`.
*/
export type OnlyInWire<Pub, Wire> = Exclude<keyof StripIndex<Wire>, keyof Pub>;

/** `true` iff `A` and `B` are mutually assignable (same value set). */
export type Equivalent<A, B> = [A] extends [B]
? [B] extends [A]
? true
: false
: false;
160 changes: 160 additions & 0 deletions sdk/tests/unit/wire-conformance-state-aware.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

// State-aware wire-type conformance oracle (Phase 2.5).
//
// The one-shot oracle (`wire-conformance.test.ts`) asserts that
// `sdk/src/types.ts` conforms to the generated wire types. This companion does
// the same for the STATE-AWARE lifecycle public types in
// `sdk/src/state-aware-types.ts`, against the generated wire state-aware defs
// (`Phase`, `IsolationConfigurationId`, `IsolationUser`, `IsolationSessionPhase`).
// Without it, a wire-model change to the state-aware surface — a new sizing
// profile, a field added to the Entra user bundle, a `Phase` change — would
// regenerate `wire.ts`, pass the codegen gate, and still leave the SDK silently
// lagging with no CI signal.
//
// Mapping note (why this is a separate file, not part of the one-shot oracle):
// the public per-phase call configs do NOT map 1:1 to a single wire type. Each
// mixes SDK-level / top-level wire fields with `IsolationSessionPhase` fields:
//
// public field wire location
// ------------------------------------ --------------------------------------
// *Config.version top-level `version` (SDK fills default)
// ProvisionConfig.filesystem top-level `Filesystem`
// ExecConfig.process top-level `Process`
// StartConfig.configurationId IsolationSessionPhase.configurationId
// {Provision,Start}Config.user IsolationSessionPhase.user / IsolationUser
//
// The top-level fields are already covered by the one-shot oracle; here we (a)
// assert the per-phase configs REUSE those same public leaf types (so the
// delegation is real, not a re-derived shape that could escape the one-shot
// oracle), and (b) directly check the genuinely state-aware shapes (the phase
// enum, the sizing-profile enum, the user bundle, and the `IsolationSessionPhase`
// field set). The runtime body is a no-op; the guarantee is enforced at `tsc`
// time.

import { test } from 'node:test';

import type { ProcessConfig, FilesystemConfig } from '../../src/types.js';

import type {
Phase,
IsolationSessionUserConfig,
IsolationSessionProvisionConfig,
IsolationSessionStartConfig,
IsolationSessionExecConfig,
IsolationSessionStopConfig,
IsolationSessionDeprovisionConfig,
} from '../../src/state-aware-types.js';

import type {
Phase as WirePhase,
IsolationUser as WireIsolationUser,
IsolationConfigurationId as WireIsolationConfigurationId,
IsolationSessionPhase as WireIsolationSessionPhase,
} from '../../src/generated/wire.js';

import type {
AssertTrue,
StripIndex,
OnlyInPublic,
OnlyInWire,
Equivalent,
} from './conformance-helpers.js';

// --- enum conformance ------------------------------------------------------

// The lifecycle phase enum must be value-for-value identical to the wire `Phase`.
type _Phase = AssertTrue<Equivalent<Phase, WirePhase>>;

// Sizing profile: the SDK exposes it inline on `startSandbox`. This is the exact
// drift case — a new wire `IsolationConfigurationId` value would otherwise be
// unrequestable through the SDK with no CI signal.
type _ConfigurationId = AssertTrue<
Equivalent<NonNullable<IsolationSessionStartConfig['configurationId']>, WireIsolationConfigurationId>
>;

// --- user bundle conformance ----------------------------------------------

// `IsolationSessionUserConfig` is a class; compare its DATA shape (the symbol
// inspect method is not part of the wire contract) to wire `IsolationUser`.
// Value equivalence alone misses a NEW OPTIONAL wire field (an optional addition
// does not break mutual assignability), so the key sets are also pinned in both
// directions: a new wire credential field (optional or required) fails
// `_UserBundleWireKeys`, and a public-only field fails `_UserBundlePublicKeys`.
type PublicUserData = Pick<IsolationSessionUserConfig, 'upn' | 'wamToken'>;
type _UserBundleVals = AssertTrue<Equivalent<PublicUserData, WireIsolationUser>>;
type _UserBundleWireKeys = AssertTrue<Equivalent<OnlyInWire<PublicUserData, WireIsolationUser>, never>>;
type _UserBundlePublicKeys = AssertTrue<Equivalent<OnlyInPublic<PublicUserData, WireIsolationUser>, never>>;

// Both phases that accept a user bundle must reuse the SAME public type, so the
// bundle check above covers them transitively.
type _ProvisionUserReuse = AssertTrue<
Equivalent<NonNullable<IsolationSessionProvisionConfig['user']>, IsolationSessionUserConfig>
>;
type _StartUserReuse = AssertTrue<
Equivalent<NonNullable<IsolationSessionStartConfig['user']>, IsolationSessionUserConfig>
>;

// --- IsolationSessionPhase field-set conformance ---------------------------

// The per-phase wire surface is DERIVED from the real public phase configs, not
// hand-restated, so a newly exposed public phase field cannot bypass the oracle
// (review finding F2). Each phase config splits into "lifted" fields that map to
// top-level wire locations (`version` is SDK metadata; `filesystem` → top-level
// `Filesystem`; `process` → top-level `Process`, all covered elsewhere) and
// backend-specific fields that map onto the wire `IsolationSessionPhase` object.
// `PublicPhaseKeys` is the union of those backend-specific keys across all five
// phase configs.
type LiftedPhaseKey = 'version' | 'filesystem' | 'process';
type PublicPhaseKeys = Exclude<
| keyof IsolationSessionProvisionConfig
| keyof IsolationSessionStartConfig
| keyof IsolationSessionExecConfig
| keyof IsolationSessionStopConfig
| keyof IsolationSessionDeprovisionConfig,
LiftedPhaseKey
>;
type WirePhaseKeys = keyof StripIndex<WireIsolationSessionPhase>;

// A public phase field with no wire `IsolationSessionPhase` counterpart fails
// (the SDK exposes a field the wire model does not define).
type _PhasePublicKeys = AssertTrue<Equivalent<Exclude<PublicPhaseKeys, WirePhaseKeys>, never>>;
// A wire `IsolationSessionPhase` field no phase config exposes fails (the wire
// model gained a per-phase field the SDK forgot to surface).
type _PhaseWireKeys = AssertTrue<Equivalent<Exclude<WirePhaseKeys, PublicPhaseKeys>, never>>;
// The per-field VALUES of the two backend-specific phase fields are pinned
// individually above: `configurationId` by `_ConfigurationId` and `user` by the
// `_UserBundle*` checks.

// --- delegation to the one-shot oracle (documented, asserted) --------------

// The per-phase configs must REUSE the public one-shot leaf types for their
// top-level fields, so the one-shot oracle already pins those shapes. If a config
// re-declared an inline shape instead, it would escape that coverage — these
// assertions fail if that ever happens.
type _ExecProcessReuse = AssertTrue<Equivalent<IsolationSessionExecConfig['process'], ProcessConfig>>;
type _ProvisionFilesystemReuse = AssertTrue<
Equivalent<NonNullable<IsolationSessionProvisionConfig['filesystem']>, FilesystemConfig>
>;

// Reference the assertion aliases so they read as intentionally load-bearing.
export type StateAwareWireConformanceAssertions = [
_Phase,
_ConfigurationId,
_UserBundleVals,
_UserBundleWireKeys,
_UserBundlePublicKeys,
_ProvisionUserReuse,
_StartUserReuse,
_PhaseWireKeys,
_PhasePublicKeys,
_ExecProcessReuse,
_ProvisionFilesystemReuse,
];

test('public state-aware SDK types conform to the generated wire schema (compile-time)', () => {
// Intentionally empty: the guarantee is enforced by the type aliases above at
// `tsc` time. If they fail to compile, `npm run build:test-unit` fails before
// this test ever runs.
});
50 changes: 8 additions & 42 deletions sdk/tests/unit/wire-conformance.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,48 +83,14 @@ import type {
TransportProtocol as WireTransportProtocol,
} from '../../src/generated/wire.js';

// --- type-level assertion helpers -----------------------------------------

/** Compiles only when `T` is exactly `true`. */
type AssertTrue<T extends true> = T;

/** Drop the `[k: string]: unknown` index signature the emitter writes on open objects. */
type StripIndex<T> = { [K in keyof T as string extends K ? never : K]: T[K] };

/**
* Recursively drop index signatures (open objects nest: e.g.
* `experimental.wslc`), so structural assignment is not tripped by an emitted
* `[k: string]: unknown` at any depth. Modifiers (`?`) are preserved because the
* mapped type is homomorphic over `keyof T`.
*/
type DeepStripIndex<T> = T extends (infer E)[]
? DeepStripIndex<E>[]
: T extends object
? { [K in keyof T as string extends K ? never : K]: DeepStripIndex<T[K]> }
: T;

/** `true` iff every value of `A` is assignable to `B` (index signatures stripped, recursively). */
type Assignable<A, B> = [A] extends [DeepStripIndex<B>] ? true : false;

/** Keys present on the public type but absent from the wire type. */
type OnlyInPublic<Pub, Wire> = Exclude<keyof Pub, keyof Wire>;

/**
* Keys present on the wire type but absent from the public type (review finding
* F1, gpt-5.5 pass). Because every generated wire field is optional,
* `Public extends Wire` stays true when the SDK simply forgets a new wire field,
* so the value/`OnlyInPublic` checks alone do NOT catch a wire-only ADDITION.
* This closes that direction. `StripIndex` drops the `[k: string]: unknown` so
* the open experimental objects don't make `keyof` collapse to `string`.
*/
type OnlyInWire<Pub, Wire> = Exclude<keyof StripIndex<Wire>, keyof Pub>;

/** `true` iff `A` and `B` are mutually assignable (same value set). */
type Equivalent<A, B> = [A] extends [B]
? [B] extends [A]
? true
: false
: false;
import type {
AssertTrue,
StripIndex,
Assignable,
OnlyInPublic,
OnlyInWire,
Equivalent,
} from './conformance-helpers.js';

// --- enum / union conformance ---------------------------------------------

Expand Down
Loading