test(01-02): add Wave 0 TDD stubs for SC-3 (Zod schemas) and SC-4 (wizard reducer)

- src/schemas/index.test.ts: 8 tests defining acceptance criteria for BACKEND_SCHEMAS (Plan 03)
- src/store/reducer.test.ts: 8 tests defining acceptance criteria for wizardReducer (Plan 04)
- Both files fail with "Cannot find module" — correct RED phase, implementation deferred to Plans 03-04
This commit is contained in:
2026-03-26 10:17:35 +01:00
parent 4c614fc06c
commit 9e28bf9ece
2 changed files with 136 additions and 0 deletions
+72
View File
@@ -0,0 +1,72 @@
import { describe, it, expect } from 'vitest';
import { BACKEND_SCHEMAS } from './index';
describe('Azure Blob Zod schema', () => {
it('accepts valid account + access key', () => {
const result = BACKEND_SCHEMAS.azureblob.safeParse({
account: 'mystorageaccount',
key: 'dGVzdGtleQ==',
});
expect(result.success).toBe(true);
});
it('accepts valid account + sas_url', () => {
const result = BACKEND_SCHEMAS.azureblob.safeParse({
account: 'mystorageaccount',
sas_url: 'https://mystorageaccount.blob.core.windows.net/?sv=2021-01-01',
});
expect(result.success).toBe(true);
});
it('rejects empty account (required field)', () => {
const result = BACKEND_SCHEMAS.azureblob.safeParse({ account: '' });
expect(result.success).toBe(false);
});
it('rejects missing account (required field)', () => {
const result = BACKEND_SCHEMAS.azureblob.safeParse({ key: 'dGVzdGtleQ==' });
expect(result.success).toBe(false);
});
});
describe('S3 Zod schema', () => {
it('accepts valid S3 credentials', () => {
const result = BACKEND_SCHEMAS.s3.safeParse({
provider: 'AWS',
access_key_id: 'AKIAIOSFODNN7EXAMPLE',
secret_access_key: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
region: 'us-east-1',
});
expect(result.success).toBe(true);
});
it('rejects missing access_key_id (required field)', () => {
const result = BACKEND_SCHEMAS.s3.safeParse({
provider: 'AWS',
secret_access_key: 'secret',
region: 'us-east-1',
});
expect(result.success).toBe(false);
});
});
describe('S3-compatible Zod schema', () => {
it('accepts valid S3-compatible credentials with endpoint', () => {
const result = BACKEND_SCHEMAS['s3-compatible'].safeParse({
provider: 'Other',
access_key_id: 'mykey',
secret_access_key: 'mysecret',
endpoint: 'https://s3.wasabisys.com',
});
expect(result.success).toBe(true);
});
it('rejects missing endpoint (required for s3-compatible)', () => {
const result = BACKEND_SCHEMAS['s3-compatible'].safeParse({
provider: 'Other',
access_key_id: 'mykey',
secret_access_key: 'mysecret',
});
expect(result.success).toBe(false);
});
});
+64
View File
@@ -0,0 +1,64 @@
import { describe, it, expect } from 'vitest';
import { wizardReducer } from './reducer';
import { INITIAL_STATE, WizardState } from './types';
describe('wizardReducer', () => {
it('returns INITIAL_STATE on first call', () => {
// @ts-expect-error intentional undefined action for initialization test
const state = wizardReducer(undefined, { type: '@@INIT' });
expect(state.currentStep).toBe(0);
expect(state.remote.backendType).toBeNull();
expect(state.remote.name).toBe('');
expect(state.remote.params).toEqual({});
expect(state.deployment.includeInstall).toBe(false);
expect(state.deployment.configPath).toBe('machine-wide');
});
it('SET_STEP updates currentStep', () => {
const state = wizardReducer(INITIAL_STATE, { type: 'SET_STEP', payload: 2 });
expect(state.currentStep).toBe(2);
});
it('SET_BACKEND_TYPE updates remote.backendType', () => {
const state = wizardReducer(INITIAL_STATE, { type: 'SET_BACKEND_TYPE', payload: 'azureblob' });
expect(state.remote.backendType).toBe('azureblob');
});
it('SET_REMOTE_NAME updates remote.name', () => {
const state = wizardReducer(INITIAL_STATE, { type: 'SET_REMOTE_NAME', payload: 'my-blob' });
expect(state.remote.name).toBe('my-blob');
});
it('SET_REMOTE_PARAMS updates remote.params', () => {
const state = wizardReducer(INITIAL_STATE, {
type: 'SET_REMOTE_PARAMS',
payload: { account: 'myaccount', key: 'mykey' },
});
expect(state.remote.params).toEqual({ account: 'myaccount', key: 'mykey' });
});
it('SET_DEPLOYMENT partially updates deployment', () => {
const state = wizardReducer(INITIAL_STATE, {
type: 'SET_DEPLOYMENT',
payload: { includeInstall: true },
});
expect(state.deployment.includeInstall).toBe(true);
expect(state.deployment.configPath).toBe('machine-wide'); // unchanged
});
it('RESET returns to INITIAL_STATE', () => {
const modified: WizardState = {
...INITIAL_STATE,
currentStep: 3,
remote: { name: 'test', backendType: 's3', params: { region: 'us-east-1' } },
};
const state = wizardReducer(modified, { type: 'RESET' });
expect(state).toEqual(INITIAL_STATE);
});
it('is a pure function — does not mutate input state', () => {
const before = { ...INITIAL_STATE };
wizardReducer(INITIAL_STATE, { type: 'SET_STEP', payload: 5 });
expect(INITIAL_STATE.currentStep).toBe(before.currentStep);
});
});