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:
@@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user