Files
astrolabe/src/core/storage-estimate.test.ts
T

105 lines
3.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, expect, test } from 'vitest';
import { humanizeBytes, jsonByteSize, summarizeStorage } from './storage-estimate';
describe('summarizeStorage (composition)', () => {
test('with origin usage: app = usage snippets datasets, total = usage', () => {
const c = summarizeStorage({ usageBytes: 1000, snippetBytes: 100, datasetBytes: 300 });
expect(c.originMeasured).toBe(true);
expect(c.totalBytes).toBe(1000);
expect(c.segments).toEqual([
{ key: 'snippets', label: 'Snippets', bytes: 100 },
{ key: 'datasets', label: 'Datasets', bytes: 300 },
{ key: 'app', label: 'App', bytes: 600 },
]);
});
test('without origin usage: snippets + datasets only, total is their sum', () => {
const c = summarizeStorage({ snippetBytes: 100, datasetBytes: 300 });
expect(c.originMeasured).toBe(false);
expect(c.totalBytes).toBe(400);
expect(c.segments.map((s) => s.key)).toEqual(['snippets', 'datasets']);
});
test('usage smaller than measured data falls back to snippets+datasets (estimate lag)', () => {
const c = summarizeStorage({ usageBytes: 50, snippetBytes: 100, datasetBytes: 300 });
expect(c.originMeasured).toBe(false);
expect(c.totalBytes).toBe(400);
expect(c.segments).toHaveLength(2);
});
test('usage exactly equal to measured data yields a zero-byte app segment', () => {
const c = summarizeStorage({ usageBytes: 400, snippetBytes: 100, datasetBytes: 300 });
expect(c.originMeasured).toBe(true);
expect(c.segments.find((s) => s.key === 'app')?.bytes).toBe(0);
});
test('invalid / negative / non-finite inputs degrade to 0 bytes', () => {
const c = summarizeStorage({ usageBytes: NaN, snippetBytes: -5, datasetBytes: Infinity });
expect(c.segments[0].bytes).toBe(0); // snippets
expect(c.segments[1].bytes).toBe(0); // datasets
expect(c.originMeasured).toBe(false); // usage NaN → unusable
expect(c.totalBytes).toBe(0);
});
test('empty workspace: every segment is zero, total zero', () => {
const c = summarizeStorage({ snippetBytes: 0, datasetBytes: 0 });
expect(c.totalBytes).toBe(0);
expect(c.segments.every((s) => s.bytes === 0)).toBe(true);
});
});
describe('jsonByteSize', () => {
test('measures the UTF-8 byte length of the JSON serialization', () => {
expect(jsonByteSize({ a: 1 })).toBe(new TextEncoder().encode('{"a":1}').length);
});
test('counts multi-byte characters by their UTF-8 size', () => {
// "é" is 2 UTF-8 bytes; JSON.stringify("é") => the 4-byte string «"é"».
expect(jsonByteSize('é')).toBe(4);
});
test('an unserializable (circular) value degrades to 0', () => {
const a: Record<string, unknown> = {};
a.self = a;
expect(jsonByteSize(a)).toBe(0);
});
});
describe('humanizeBytes', () => {
test('zero renders as whole bytes', () => {
expect(humanizeBytes(0)).toBe('0 B');
});
test('small values stay in whole bytes', () => {
expect(humanizeBytes(1)).toBe('1 B');
expect(humanizeBytes(512)).toBe('512 B');
expect(humanizeBytes(1023)).toBe('1023 B');
});
test('KB boundary (1024) and within KB', () => {
expect(humanizeBytes(1024)).toBe('1.0 KB');
expect(humanizeBytes(1536)).toBe('1.5 KB');
});
test('MB boundary and within MB', () => {
expect(humanizeBytes(1024 * 1024)).toBe('1.0 MB');
expect(humanizeBytes(5 * 1024 * 1024)).toBe('5.0 MB');
expect(humanizeBytes(2.5 * 1024 * 1024)).toBe('2.5 MB');
});
test('GB boundary', () => {
expect(humanizeBytes(1024 * 1024 * 1024)).toBe('1.0 GB');
});
test('caps at the largest unit (TB)', () => {
expect(humanizeBytes(1024 ** 4)).toBe('1.0 TB');
expect(humanizeBytes(1024 ** 5)).toBe('1024.0 TB');
});
test('negative and non-finite inputs degrade to 0 B', () => {
expect(humanizeBytes(-1)).toBe('0 B');
expect(humanizeBytes(NaN)).toBe('0 B');
expect(humanizeBytes(Infinity)).toBe('0 B');
});
});