json-document.test.ts•17.2 kB
/**
 * Tests for v2 JSON document schemas
 */
import {
  DocumentMetadataV2Schema,
  BaseJsonDocumentV2Schema,
  SCHEMA_VERSION
} from '../../../src/v2/json-document.js'; // 相対パスに変更
import {
  BranchContextJsonV2Schema,
  ActiveContextJsonV2Schema,
  ProgressJsonV2Schema,
  SystemPatternsJsonV2Schema,
  GenericDocumentJsonV2Schema,
} from '../../../src/document-types/index.js'; // 相対パスに変更
describe('DocumentMetadataV2Schema', () => {
  it('should validate correct metadata', () => {
    const validMetadata = {
      title: 'Test Document',
      documentType: 'test',
      id: '123e4567-e89b-12d3-a456-426614174000',
      path: 'test/document.json',
      tags: ['test', 'example'],
      lastModified: '2023-10-27T10:00:00.000Z', // Use ISO string
      createdAt: '2023-10-26T10:00:00.000Z', // Use ISO string
      version: 1
    };
    const result = DocumentMetadataV2Schema.safeParse(validMetadata);
    expect(result.success).toBe(true);
  });
  it('should accept ISO date strings for dates', () => {
    const validMetadata = {
      title: 'Test Document',
      documentType: 'test',
      id: '123e4567-e89b-12d3-a456-426614174000',
      path: 'test/document.json',
      tags: ['test', 'example'],
      lastModified: '2025-03-27T12:00:00Z',
      createdAt: '2025-03-26T10:00:00Z',
      version: 1
    };
    const result = DocumentMetadataV2Schema.safeParse(validMetadata);
    expect(result.success).toBe(true);
  });
  it('should reject metadata with missing required fields', () => {
    const invalidMetadata = {
      title: 'Test Document',
      // missing documentType, id, path, etc.
    };
    const result = DocumentMetadataV2Schema.safeParse(invalidMetadata);
    expect(result.success).toBe(false);
    if (!result.success) {
      expect(result.error.issues.length).toBeGreaterThan(0);
    }
  });
  it('should reject metadata with invalid UUID', () => {
    const invalidMetadata = {
      title: 'Test Document',
      documentType: 'test',
      id: 'not-a-uuid', // Invalid UUID
      path: 'test/document.json',
      tags: ['test'],
      lastModified: new Date().toISOString(), // Use ISO string
      createdAt: new Date().toISOString(), // Use ISO string
      version: 1
    };
    const result = DocumentMetadataV2Schema.safeParse(invalidMetadata);
    expect(result.success).toBe(false);
  });
  it('should reject metadata with invalid tags', () => {
    const invalidMetadata = {
      title: 'Test Document',
      documentType: 'test',
      id: '123e4567-e89b-12d3-a456-426614174000',
      path: 'test/document.json',
      tags: ['Invalid Tag', 'test'], // Invalid tag with uppercase and space
      lastModified: new Date().toISOString(), // Use ISO string
      createdAt: new Date().toISOString(), // Use ISO string
      version: 1
    };
    const result = DocumentMetadataV2Schema.safeParse(invalidMetadata);
    expect(result.success).toBe(false);
  });
});
describe('BaseJsonDocumentV2Schema', () => {
  it('should validate a correct base document structure (schema and documentType)', () => {
    // Base schema now only defines schema and documentType at the top level
    const validBaseStructure = {
      schema: SCHEMA_VERSION,
      documentType: 'any_valid_type_string', // Needs a non-empty string
      // metadata and content are not part of the base schema itself anymore
    };
    const result = BaseJsonDocumentV2Schema.safeParse(validBaseStructure);
    // If this fails, log the Zod error details
    if (!result.success) {
      console.error("BaseJsonDocumentV2Schema validation failed:", JSON.stringify(result.error.format(), null, 2));
    }
    expect(result.success).toBe(true);
  });
  it('should reject document with wrong schema version', () => {
    const invalidDocument = {
      schema: 'wrong_version',
      metadata: {
        title: 'Test Document',
        documentType: 'test',
        id: '123e4567-e89b-12d3-a456-426614174000',
        path: 'test/document.json',
        tags: ['test'],
        lastModified: new Date().toISOString(), // Use ISO string
        createdAt: new Date().toISOString(), // Use ISO string
        version: 1
      },
      content: {
        key: 'value'
      }
    };
    const result = BaseJsonDocumentV2Schema.safeParse(invalidDocument);
    expect(result.success).toBe(false);
  });
  it('should reject document with empty content', () => {
    const invalidDocument = {
      schema: SCHEMA_VERSION,
      metadata: {
        title: 'Test Document',
        documentType: 'test',
        id: '123e4567-e89b-12d3-a456-426614174000',
        path: 'test/document.json',
        tags: ['test'],
        lastModified: new Date().toISOString(), // Use ISO string
        createdAt: new Date().toISOString(), // Use ISO string
        version: 1
      },
      content: {}  // Empty content
    };
    const result = BaseJsonDocumentV2Schema.safeParse(invalidDocument);
    // Base schema requires content not to be empty
    expect(result.success).toBe(false);
  });
});
describe('BranchContextJsonV2Schema', () => {
  it('should validate a correct branch context document', () => {
    const validDocument = {
      schema: SCHEMA_VERSION,
      documentType: 'branch_context' as const, // Move documentType to top level
      metadata: {
        title: 'Branch Context',
        // documentType: 'branch_context', // Removed from metadata
        id: '123e4567-e89b-12d3-a456-426614174000',
        path: 'branchContext.json',
        tags: ['branch-context'],
        lastModified: '2023-10-27T12:00:00.000Z',
        createdAt: '2023-10-26T12:00:00.000Z',
        version: 1
      },
      content: {
        purpose: 'Feature implementation',
        background: 'Some background info', // Optional field
        userStories: [
          {
            description: 'User can do something',
            completed: false // Default is false, explicitly setting is fine
          }
        ]
        // additionalNotes is missing, but it's optional in the schema
      }
    };
    const result = BranchContextJsonV2Schema.safeParse(validDocument);
    expect(result.success).toBe(true);
  });
  it('should reject branch context with wrong document type', () => {
    const invalidDocument = {
      schema: SCHEMA_VERSION,
      documentType: 'wrong_type' as any, // Invalid literal at top level
      metadata: {
        title: 'Branch Context',
        // documentType: 'wrong_type', // Removed from metadata
        id: '123e4567-e89b-12d3-a456-426614174000',
        path: 'branchContext.json',
        tags: ['branch-context'],
        lastModified: new Date().toISOString(), // Use ISO string
        createdAt: new Date().toISOString(), // Use ISO string
        version: 1
      },
      content: {
        purpose: 'Feature implementation',
        userStories: []
      }
    };
    const result = BranchContextJsonV2Schema.safeParse(invalidDocument);
    expect(result.success).toBe(false);
  });
  it('should reject branch context without required purpose', () => {
    const invalidDocument = {
      schema: SCHEMA_VERSION,
      documentType: 'branch_context' as const, // Correct type at top level
      metadata: {
        title: 'Branch Context',
        // documentType: 'branch_context', // Removed from metadata
        id: '123e4567-e89b-12d3-a456-426614174000',
        path: 'branchContext.json',
        tags: ['branch-context'],
        lastModified: new Date().toISOString(), // Use ISO string
        createdAt: new Date().toISOString(), // Use ISO string
        version: 1
      },
      content: {
        // Missing required field 'purpose'
        userStories: []
      }
    };
    const result = BranchContextJsonV2Schema.safeParse(invalidDocument);
    expect(result.success).toBe(false);
  });
});
describe('ActiveContextJsonV2Schema', () => {
  it('should validate a correct active context document', () => {
    const validDocument = {
      schema: SCHEMA_VERSION,
      documentType: 'active_context' as const, // Move documentType to top level
      metadata: {
        title: 'Active Context',
        // documentType: 'active_context', // Removed from metadata
        id: '123e4567-e89b-12d3-a456-426614174000',
        path: 'activeContext.json',
        tags: ['active-context'],
        lastModified: '2023-10-27T13:00:00.000Z',
        createdAt: '2023-10-26T13:00:00.000Z',
        version: 1
      },
      content: {
        currentWork: 'Working on feature X', // Optional
        recentChanges: [], // Optional, default []
        activeDecisions: [], // Optional, default []
        considerations: [], // Optional, default []
        nextSteps: [] // Optional, default []
      }
    };
    const result = ActiveContextJsonV2Schema.safeParse(validDocument);
    expect(result.success).toBe(true);
  });
  it('should accept active context with only optional fields', () => {
    const validDocument = {
      schema: SCHEMA_VERSION,
      documentType: 'active_context' as const, // Move documentType to top level
      metadata: {
        title: 'Active Context',
        // documentType: 'active_context', // Removed from metadata
        id: '123e4567-e89b-12d3-a456-426614174000',
        path: 'activeContext.json',
        tags: ['active-context'],
        lastModified: '2023-10-27T14:00:00.000Z',
        createdAt: '2023-10-26T14:00:00.000Z',
        version: 1
      },
      content: {
        // All content fields are optional or have defaults in ActiveContext schema
      }
    };
    const result = ActiveContextJsonV2Schema.safeParse(validDocument);
    expect(result.success).toBe(true);
  });
});
describe('ProgressJsonV2Schema', () => {
  it('should validate a correct progress document', () => {
    const validDocument = {
      schema: SCHEMA_VERSION,
      documentType: 'progress' as const, // Move documentType to top level
      metadata: {
        title: 'Progress',
        // documentType: 'progress', // Removed from metadata
        id: '123e4567-e89b-12d3-a456-426614174000',
        path: 'progress.json',
        tags: ['progress'],
        lastModified: '2023-10-27T15:00:00.000Z',
        createdAt: '2023-10-26T15:00:00.000Z',
        version: 1
      },
      content: {
        workingFeatures: [], // Optional, default []
        pendingImplementation: [], // Optional, default []
        status: 'In progress', // Optional
        // currentState: 'Phase 2', // This field is not in the Progress schema
        completionPercentage: 50, // Optional
        knownIssues: [] // Optional, default []
      }
    };
    const result = ProgressJsonV2Schema.safeParse(validDocument);
    expect(result.success).toBe(true);
  });
  it('should accept progress document with minimum required fields', () => {
    // status and completionPercentage are required according to the schema
    const validDocument = {
      schema: SCHEMA_VERSION,
      documentType: 'progress' as const,
      metadata: {
        title: 'Progress',
        id: '123e4567-e89b-12d3-a456-426614174000',
        path: 'progress.json',
        tags: ['progress'],
        lastModified: '2023-10-27T16:00:00.000Z',
        createdAt: '2023-10-26T16:00:00.000Z',
        version: 1
      },
      content: {
        status: 'Initial', // Add required status
        completionPercentage: 0, // Add required completionPercentage
        // Optional fields with defaults can be omitted or explicitly set
        workingFeatures: [],
        pendingImplementation: [],
        knownIssues: [],
        // currentState is optional
      }
    };
    const result = ProgressJsonV2Schema.safeParse(validDocument);
    if (!result.success) {
      console.error("ProgressJsonV2Schema (minimum) validation failed:", JSON.stringify(result.error.format(), null, 2));
    }
    expect(result.success).toBe(true);
  });
});
describe('SystemPatternsJsonV2Schema', () => {
  it('should validate a correct system patterns document', () => {
    const validDocument = {
      schema: SCHEMA_VERSION,
      documentType: 'system_patterns' as const, // Move documentType to top level
      metadata: {
        title: 'System Patterns',
        // documentType: 'system_patterns', // Removed from metadata
        id: '123e4567-e89b-12d3-a456-426614174000',
        path: 'systemPatterns.json',
        tags: ['system-patterns'],
        lastModified: '2023-10-27T17:00:00.000Z',
        createdAt: '2023-10-26T17:00:00.000Z',
        version: 1
      },
      content: {
        technicalDecisions: [ // Optional, default []
          {
            title: 'Use TypeScript', // Required
            context: 'Need type safety', // Required
            decision: 'We will use TypeScript', // Required
            consequences: ['Better code quality', 'Requires compilation'] // Must be string[] with min 1 element
            // status, date, alternatives are not in the schema definition
          }
        ],
        implementationPatterns: [] // Optional, default []
      }
    };
    const result = SystemPatternsJsonV2Schema.safeParse(validDocument);
    if (!result.success) {
      console.error("SystemPatternsJsonV2Schema validation failed:", JSON.stringify(result.error.format(), null, 2));
    }
    expect(result.success).toBe(true);
  });
  it('should accept system patterns with minimum fields', () => {
    const validDocument = {
      schema: SCHEMA_VERSION,
      documentType: 'system_patterns' as const, // Move documentType to top level
      metadata: {
        title: 'System Patterns',
        // documentType: 'system_patterns', // Removed from metadata
        id: '123e4567-e89b-12d3-a456-426614174000',
        path: 'systemPatterns.json',
        tags: ['system-patterns'],
        lastModified: '2023-10-27T18:00:00.000Z',
        createdAt: '2023-10-26T18:00:00.000Z',
        version: 1
      },
      content: {
        // technicalDecisions and implementationPatterns have defaults
      }
    };
    const result = SystemPatternsJsonV2Schema.safeParse(validDocument);
    expect(result.success).toBe(true);
  });
  it('should reject technical decision with missing required fields (e.g., title)', () => { // テストケース名を修正
    const invalidDocument = {
      schema: SCHEMA_VERSION,
      documentType: 'system_patterns' as const, // Move documentType to top level
      metadata: {
        title: 'System Patterns',
        // documentType: 'system_patterns', // Removed from metadata
        id: '123e4567-e89b-12d3-a456-426614174000',
        path: 'systemPatterns.json',
        tags: ['system-patterns'],
        lastModified: new Date().toISOString(), // Use ISO string
        createdAt: new Date().toISOString(), // Use ISO string
        version: 1
      },
      content: {
        technicalDecisions: [
          {
            // title: 'Use TypeScript', // title を削除して必須フィールド欠落をテスト
            context: 'Need type safety',
            decision: 'We will use TypeScript',
            // consequences は TechnicalDecisionContentV2Schema では string[] なので修正
            consequences: ['Consequence 1'],
            // date は TechnicalDecisionContentV2Schema にないので削除
            // date: new Date().toISOString(),
          }
        ]
      }
    };
    const result = SystemPatternsJsonV2Schema.safeParse(invalidDocument);
    expect(result.success).toBe(false);
    // title が欠落しているエラーを確認 (パスは content.technicalDecisions[0].title になるはず)
    expect(result.error?.errors.some((e: any) => e.path.includes('title'))).toBe(true);
  });
});
describe('GenericDocumentJsonV2Schema', () => {
  it('should validate a generic document', () => {
    // Generic schema expects documentType inside metadata
    const validDocument = {
      schema: SCHEMA_VERSION,
      documentType: 'custom_type', // トップレベルに移動
      metadata: {
        title: 'Custom Document',
        // documentType: 'custom_type', // metadata から削除
        id: '123e4567-e89b-12d3-a456-426614174000',
        path: 'custom.json',
        tags: ['custom'],
        lastModified: '2023-10-27T19:00:00.000Z',
        createdAt: '2023-10-26T19:00:00.000Z',
        version: 1
      },
      content: { // Content must not be empty
        customField: 'value',
        anotherField: 123
      }
    };
    const result = GenericDocumentJsonV2Schema.safeParse(validDocument);
    expect(result.success).toBe(true);
  });
  it('should reject generic document with empty content', () => {
    const invalidDocument = {
      schema: SCHEMA_VERSION,
      metadata: {
        title: 'Custom Document',
        documentType: 'custom_type',
        id: '123e4567-e89b-12d3-a456-426614174000',
        path: 'custom.json',
        tags: ['custom'],
        lastModified: new Date().toISOString(), // Use ISO string
        createdAt: new Date().toISOString(), // Use ISO string
        version: 1
      },
      content: {} // Empty content
    };
    const result = GenericDocumentJsonV2Schema.safeParse(invalidDocument);
    // Generic schema requires content not to be empty
    expect(result.success).toBe(false);
  });
});