/**
* Tests for filename parsing utilities
* Based on Julia code examples and logic
*/
import { describe, it, expect } from 'vitest';
import { dateTimeOfFilename, parseClusterFilenames } from '@/utils/filenameParser';
describe('Filename Parsing', () => {
describe('dateTimeOfFilename', () => {
it('should parse YYMMDD format (test case a)', () => {
const filenames = ["201012_123456.wav", "201014_123456.WAV", "201217_123456.wav", "211122_123456.WAV"];
const result = dateTimeOfFilename(filenames);
expect(result).toHaveLength(4);
// Year 20 should be interpreted as 2020 (less variance than days)
expect(result[0].getFullYear()).toBe(2020);
expect(result[0].getMonth()).toBe(9); // October (0-indexed)
expect(result[0].getDate()).toBe(12);
expect(result[0].getHours()).toBe(12);
expect(result[0].getMinutes()).toBe(34);
expect(result[0].getSeconds()).toBe(56);
expect(result[1].getFullYear()).toBe(2020);
expect(result[1].getMonth()).toBe(9); // October
expect(result[1].getDate()).toBe(14);
expect(result[2].getFullYear()).toBe(2020);
expect(result[2].getMonth()).toBe(11); // December
expect(result[2].getDate()).toBe(17);
expect(result[3].getFullYear()).toBe(2021);
expect(result[3].getMonth()).toBe(10); // November
expect(result[3].getDate()).toBe(22);
});
it('should parse DDMMYY format (test case b)', () => {
const filenames = ["121020_123456.WAV", "141020_123456.wav", "171220_123456.WAV", "221121_123456.wav"];
const result = dateTimeOfFilename(filenames);
expect(result).toHaveLength(4);
// More variance in first two digits (12,14,17,22) than last two (20,20,20,21)
// So DDMMYY format: day=first, month=middle, year=last+2000
expect(result[0].getDate()).toBe(12);
expect(result[0].getMonth()).toBe(9); // October (0-indexed)
expect(result[0].getFullYear()).toBe(2020);
expect(result[1].getDate()).toBe(14);
expect(result[1].getMonth()).toBe(9); // October
expect(result[1].getFullYear()).toBe(2020);
expect(result[2].getDate()).toBe(17);
expect(result[2].getMonth()).toBe(11); // December
expect(result[2].getFullYear()).toBe(2020);
expect(result[3].getDate()).toBe(22);
expect(result[3].getMonth()).toBe(10); // November
expect(result[3].getFullYear()).toBe(2021);
});
it('should parse YYYYMMDD format (test case c)', () => {
const filenames = ["20230609_103000.WAV", "20241109_201504.wav"];
const result = dateTimeOfFilename(filenames);
expect(result).toHaveLength(2);
expect(result[0].getFullYear()).toBe(2023);
expect(result[0].getMonth()).toBe(5); // June (0-indexed)
expect(result[0].getDate()).toBe(9);
expect(result[0].getHours()).toBe(10);
expect(result[0].getMinutes()).toBe(30);
expect(result[0].getSeconds()).toBe(0);
expect(result[1].getFullYear()).toBe(2024);
expect(result[1].getMonth()).toBe(10); // November
expect(result[1].getDate()).toBe(9);
expect(result[1].getHours()).toBe(20);
expect(result[1].getMinutes()).toBe(15);
expect(result[1].getSeconds()).toBe(4);
});
it('should parse mixed 6-digit dates with variance detection (test case d)', () => {
const filenames = [
"120119_003002.wav",
"180120_231502.wav",
"170122_010005.wav",
"010419_234502.WAV",
"310320_231502.wav",
"220824_231502.WAV",
"240123_231502.wav",
];
const result = dateTimeOfFilename(filenames);
expect(result).toHaveLength(7);
// First two digits: 12,18,17,01,31,22,24 (variance = high)
// Last two digits: 19,20,22,19,20,24,23 (variance = lower)
// Should be DDMMYY format
expect(result[0].getDate()).toBe(12);
expect(result[0].getMonth()).toBe(0); // January (0-indexed)
expect(result[0].getFullYear()).toBe(2019);
expect(result[0].getHours()).toBe(0);
expect(result[0].getMinutes()).toBe(30);
expect(result[0].getSeconds()).toBe(2);
expect(result[1].getDate()).toBe(18);
expect(result[1].getMonth()).toBe(0); // January
expect(result[1].getFullYear()).toBe(2020);
expect(result[4].getDate()).toBe(31);
expect(result[4].getMonth()).toBe(2); // March
expect(result[4].getFullYear()).toBe(2020);
});
it('should parse filenames with prefixes (test case e)', () => {
const filenames = ["XYZ123_7689_20230609_103000.WAV", "string 20241109_201504.wav"];
const result = dateTimeOfFilename(filenames);
expect(result).toHaveLength(2);
expect(result[0].getFullYear()).toBe(2023);
expect(result[0].getMonth()).toBe(5); // June
expect(result[0].getDate()).toBe(9);
expect(result[0].getHours()).toBe(10);
expect(result[0].getMinutes()).toBe(30);
expect(result[0].getSeconds()).toBe(0);
expect(result[1].getFullYear()).toBe(2024);
expect(result[1].getMonth()).toBe(10); // November
expect(result[1].getDate()).toBe(9);
expect(result[1].getHours()).toBe(20);
expect(result[1].getMinutes()).toBe(15);
expect(result[1].getSeconds()).toBe(4);
});
it('should parse filenames with complex prefixes (test case f)', () => {
const filenames = [
"abcdefg__1234_180120_231502.wav",
"string 120119_003002.wav",
"ABCD EFG___170122_010005.wav",
"BHD_1234 010419_234502.WAV",
"cill xyz 310320_231502.wav",
"220824_231502.WAV",
"240123_231502.wav",
];
const result = dateTimeOfFilename(filenames);
expect(result).toHaveLength(7);
// Same pattern as test case d - should be DDMMYY
expect(result[0].getDate()).toBe(18);
expect(result[0].getMonth()).toBe(0); // January
expect(result[0].getFullYear()).toBe(2020);
expect(result[0].getHours()).toBe(23);
expect(result[0].getMinutes()).toBe(15);
expect(result[0].getSeconds()).toBe(2);
expect(result[1].getDate()).toBe(12);
expect(result[1].getMonth()).toBe(0); // January
expect(result[1].getFullYear()).toBe(2019);
expect(result[4].getDate()).toBe(31);
expect(result[4].getMonth()).toBe(2); // March
expect(result[4].getFullYear()).toBe(2020);
});
it('should throw error for empty filename array', () => {
expect(() => dateTimeOfFilename([])).toThrow("This is an empty vector of filenames");
});
it('should throw error for filenames without date patterns', () => {
expect(() => dateTimeOfFilename(["invalid_filename.wav"])).toThrow("No date_time pattern found in filename");
});
it('should throw error for mixed date formats', () => {
const mixedFormats = ["201012_123456.wav", "20231012_123456.wav"]; // 6-digit vs 8-digit
expect(() => dateTimeOfFilename(mixedFormats)).toThrow("Different date formats in vector");
});
it('should throw error for wrong length patterns', () => {
const wrongLength = ["2010_123456.wav"]; // 4 digits instead of 6 or 8
expect(() => dateTimeOfFilename(wrongLength)).toThrow("No date_time pattern found in filename");
});
it('should throw error when not enough files for 6-digit disambiguation', () => {
const singleFile = ["120119_003002.wav"];
expect(() => dateTimeOfFilename(singleFile)).toThrow("Not enough files to work out YYMMDD v DDMMYY");
});
});
describe('parseClusterFilenames', () => {
it('should parse filenames and format timestamps', () => {
const filenames = ["201012_123456.wav", "201014_123456.WAV"];
const result = parseClusterFilenames(filenames, "UTC");
expect(result).toHaveLength(2);
expect(result[0].fileName).toBe("201012_123456.wav");
expect(result[0].timestamp.timestampLocal).toMatch(/^2020-10-12T12:34:56\.000\+00:00$/);
expect(result[0].timestamp.timestampUTC).toMatch(/^2020-10-12T12:34:56\.000Z$/);
expect(result[1].fileName).toBe("201014_123456.WAV");
expect(result[1].timestamp.timestampLocal).toMatch(/^2020-10-14T12:34:56\.000\+00:00$/);
});
it('should handle UTC timezone', () => {
const filenames = ["20230609_103000.WAV"];
const result = parseClusterFilenames(filenames, "UTC");
expect(result).toHaveLength(1);
expect(result[0].timestamp.timestampLocal).toBe("2023-06-09T10:30:00.000+00:00");
expect(result[0].timestamp.timestampUTC).toBe("2023-06-09T10:30:00.000Z");
});
it('should throw error for empty filenames array', () => {
expect(() => parseClusterFilenames([], "UTC")).toThrow("No filenames provided");
});
it('should process multiple files consistently', () => {
// Test that all files are processed with consistent formatting
const filenames = ["120119_003002.wav", "310320_231502.wav"];
const result = parseClusterFilenames(filenames, "UTC");
expect(result).toHaveLength(2);
// Check first file - 120119 interpreted as YYMMDD (2012-01-19)
expect(result[0].fileName).toBe("120119_003002.wav");
expect(result[0].timestamp.timestampLocal).toMatch(/^2012-01-19T00:30:02\.000\+00:00$/);
expect(result[0].timestamp.timestampUTC).toMatch(/^2012-01-19T00:30:02\.000Z$/);
// Check second file - 310320 interpreted as YYMMDD (2031-03-20)
expect(result[1].fileName).toBe("310320_231502.wav");
expect(result[1].timestamp.timestampLocal).toMatch(/^2031-03-20T23:15:02\.000\+00:00$/);
expect(result[1].timestamp.timestampUTC).toMatch(/^2031-03-20T23:15:02\.000Z$/);
});
});
describe('Edge Cases', () => {
it('should handle invalid time components gracefully', () => {
// This would need to be handled by the calling code since our regex should catch this
// but adding for completeness
const invalidTime = ["201012_256090.wav", "201013_123456.wav"]; // Invalid hour/minute/second but multiple files
expect(() => dateTimeOfFilename(invalidTime)).not.toThrow();
// The Date constructor will handle invalid dates by creating an invalid Date object
});
it('should handle case-insensitive file extensions', () => {
const mixedCase = ["201012_123456.wav", "201014_123456.WAV", "201217_123456.Wav"];
const result = dateTimeOfFilename(mixedCase);
expect(result).toHaveLength(3);
});
});
});