mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-19 13:36:48 +00:00
724c88c201
A mechanical prettier --write over server/src, server/tests, server/scripts and shared/src with the workspaces' .prettierrc; nothing but layout and import order changes, and every file keeps the same set of imports. shared is formatted throughout and its format baseline is empty. The server baseline keeps the 35 files that Prettier would grow past their lint:size limit (they need a split first, which is not a formatting change), and the generated entity files stay as their generator writes them.
176 lines
7.3 KiB
TypeScript
176 lines
7.3 KiB
TypeScript
import { decideRange, isNotModified, validatorsFor, type Validators } from '../../../../src/nest/storage/http-serving';
|
|
|
|
import { describe, it, expect } from 'vitest';
|
|
|
|
// The pure half of the remote-serving parity work (task C4): every decision a
|
|
// proxying byte-path makes BEFORE it touches a driver or a socket. Kept
|
|
// dependency-free so the state machine is provable in isolation — the
|
|
// service-level suite then only has to prove the wiring.
|
|
|
|
const LM_5000 = new Date(5000).toUTCString();
|
|
|
|
describe('validatorsFor', () => {
|
|
it('prefers the driver-supplied ETag verbatim', () => {
|
|
expect(validatorsFor({ key: 'a.bin', size: 10, mtimeMs: 5000, etag: '"abc123"' })).toEqual({
|
|
etag: '"abc123"',
|
|
lastModified: LM_5000,
|
|
});
|
|
});
|
|
|
|
it('derives a weak size-mtime ETag when the driver supplies none (send/express parity)', () => {
|
|
expect(validatorsFor({ key: 'a.bin', size: 26, mtimeMs: 4096 })).toEqual({
|
|
etag: 'W/"1a-1000"',
|
|
lastModified: new Date(4096).toUTCString(),
|
|
});
|
|
});
|
|
|
|
it('emits NO validators at all when mtimeMs is 0 (S3 can report it — never a 1970 header)', () => {
|
|
expect(validatorsFor({ key: 'a.bin', size: 10, mtimeMs: 0 })).toEqual({});
|
|
});
|
|
|
|
it('still emits a driver ETag when mtimeMs is 0, but no Last-Modified', () => {
|
|
expect(validatorsFor({ key: 'a.bin', size: 10, mtimeMs: 0, etag: '"abc123"' })).toEqual({
|
|
etag: '"abc123"',
|
|
});
|
|
});
|
|
|
|
it('ignores an empty driver ETag and falls back to the derived one', () => {
|
|
expect(validatorsFor({ key: 'a.bin', size: 1, mtimeMs: 16, etag: '' }).etag).toBe('W/"1-10"');
|
|
});
|
|
|
|
it('floors a fractional mtime into the derived tag', () => {
|
|
expect(validatorsFor({ key: 'a.bin', size: 1, mtimeMs: 16.9 }).etag).toBe('W/"1-10"');
|
|
});
|
|
});
|
|
|
|
describe('isNotModified', () => {
|
|
const v: Validators = { etag: '"abc"', lastModified: LM_5000 };
|
|
|
|
it('matches If-None-Match exactly', () => {
|
|
expect(isNotModified({ 'if-none-match': '"abc"' }, v)).toBe(true);
|
|
expect(isNotModified({ 'if-none-match': '"other"' }, v)).toBe(false);
|
|
});
|
|
|
|
it('matches a list of candidate tags', () => {
|
|
expect(isNotModified({ 'if-none-match': '"x", "abc" ,"y"' }, v)).toBe(true);
|
|
});
|
|
|
|
it('compares weakly — W/ prefixes on either side are stripped', () => {
|
|
expect(isNotModified({ 'if-none-match': 'W/"abc"' }, v)).toBe(true);
|
|
expect(isNotModified({ 'if-none-match': '"1a-1000"' }, { etag: 'W/"1a-1000"' })).toBe(true);
|
|
});
|
|
|
|
it('honours the * wildcard', () => {
|
|
expect(isNotModified({ 'if-none-match': '*' }, v)).toBe(true);
|
|
expect(isNotModified({ 'if-none-match': '*' }, {})).toBe(true);
|
|
});
|
|
|
|
it('never matches when we have no ETag to compare against', () => {
|
|
expect(isNotModified({ 'if-none-match': '"abc"' }, {})).toBe(false);
|
|
});
|
|
|
|
it('lets If-None-Match win over If-Modified-Since even when the date says fresh', () => {
|
|
expect(isNotModified({ 'if-none-match': '"stale"', 'if-modified-since': LM_5000 }, v)).toBe(false);
|
|
});
|
|
|
|
it('matches If-Modified-Since at second granularity', () => {
|
|
// 5400ms truncates to the same second as 5000ms — a 1970-second-boundary
|
|
// stand-in for the sub-second mtimes Last-Modified cannot express.
|
|
expect(isNotModified({ 'if-modified-since': LM_5000 }, { lastModified: new Date(5400).toUTCString() })).toBe(true);
|
|
expect(isNotModified({ 'if-modified-since': LM_5000 }, { lastModified: new Date(6000).toUTCString() })).toBe(false);
|
|
});
|
|
|
|
it('treats an older If-Modified-Since as modified', () => {
|
|
expect(isNotModified({ 'if-modified-since': new Date(4000).toUTCString() }, v)).toBe(false);
|
|
});
|
|
|
|
it('ignores an unparseable If-Modified-Since', () => {
|
|
expect(isNotModified({ 'if-modified-since': 'not-a-date' }, v)).toBe(false);
|
|
});
|
|
|
|
it('is false when there is no Last-Modified to compare (mtimeMs 0)', () => {
|
|
expect(isNotModified({ 'if-modified-since': LM_5000 }, {})).toBe(false);
|
|
});
|
|
|
|
it('is false with no conditional headers at all', () => {
|
|
expect(isNotModified({}, v)).toBe(false);
|
|
});
|
|
|
|
it('reads the first value of a repeated header', () => {
|
|
expect(isNotModified({ 'if-none-match': ['"abc"', '"zzz"'] }, v)).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('decideRange', () => {
|
|
it('is full with no Range header', () => {
|
|
expect(decideRange({}, 10, '"abc"')).toEqual({ kind: 'full' });
|
|
});
|
|
|
|
it('resolves a closed range inclusively', () => {
|
|
expect(decideRange({ range: 'bytes=2-5' }, 10, '"abc"')).toEqual({ kind: 'partial', start: 2, end: 5 });
|
|
});
|
|
|
|
it('resolves an open-ended range to the last byte', () => {
|
|
expect(decideRange({ range: 'bytes=5-' }, 10, '"abc"')).toEqual({ kind: 'partial', start: 5, end: 9 });
|
|
});
|
|
|
|
it('clamps an end past EOF', () => {
|
|
expect(decideRange({ range: 'bytes=5-999' }, 10, '"abc"')).toEqual({ kind: 'partial', start: 5, end: 9 });
|
|
});
|
|
|
|
it('computes a suffix range from the pre-stat size', () => {
|
|
expect(decideRange({ range: 'bytes=-4' }, 10, '"abc"')).toEqual({ kind: 'partial', start: 6, end: 9 });
|
|
});
|
|
|
|
it('clamps a suffix larger than the object to the whole object', () => {
|
|
expect(decideRange({ range: 'bytes=-999' }, 10, '"abc"')).toEqual({ kind: 'partial', start: 0, end: 9 });
|
|
});
|
|
|
|
it('is unsatisfiable past EOF, on an inverted range, and on a zero-length suffix', () => {
|
|
expect(decideRange({ range: 'bytes=10-12' }, 10, '"abc"')).toEqual({ kind: 'unsatisfiable' });
|
|
expect(decideRange({ range: 'bytes=5-3' }, 10, '"abc"')).toEqual({ kind: 'unsatisfiable' });
|
|
expect(decideRange({ range: 'bytes=-0' }, 10, '"abc"')).toEqual({ kind: 'unsatisfiable' });
|
|
});
|
|
|
|
it('is unsatisfiable for any range against a zero-byte object', () => {
|
|
expect(decideRange({ range: 'bytes=0-' }, 0, '"abc"')).toEqual({ kind: 'unsatisfiable' });
|
|
expect(decideRange({ range: 'bytes=-1' }, 0, '"abc"')).toEqual({ kind: 'unsatisfiable' });
|
|
});
|
|
|
|
it('falls back to a full 200 for a multi-range request (no multipart/byteranges)', () => {
|
|
expect(decideRange({ range: 'bytes=0-1,4-5' }, 10, '"abc"')).toEqual({ kind: 'full' });
|
|
});
|
|
|
|
it('falls back to a full 200 for malformed or non-byte units', () => {
|
|
for (const range of ['bytes=-', 'bytes=abc', 'items=0-1', 'bytes 0-1', 'bytes=0-1;x', '']) {
|
|
expect(decideRange({ range }, 10, '"abc"')).toEqual({ kind: 'full' });
|
|
}
|
|
});
|
|
|
|
it('honours a strong-exact If-Range match', () => {
|
|
expect(decideRange({ range: 'bytes=2-5', 'if-range': '"abc"' }, 10, '"abc"')).toEqual({
|
|
kind: 'partial',
|
|
start: 2,
|
|
end: 5,
|
|
});
|
|
});
|
|
|
|
it('serves the full entity when If-Range does not strong-match', () => {
|
|
expect(decideRange({ range: 'bytes=2-5', 'if-range': '"stale"' }, 10, '"abc"')).toEqual({ kind: 'full' });
|
|
// A weak validator can never satisfy If-Range (RFC 9110 §13.1.5).
|
|
expect(decideRange({ range: 'bytes=2-5', 'if-range': 'W/"abc"' }, 10, 'W/"abc"')).toEqual({ kind: 'full' });
|
|
// Neither can an HTTP-date form — we only carry the ETag here.
|
|
expect(decideRange({ range: 'bytes=2-5', 'if-range': LM_5000 }, 10, '"abc"')).toEqual({ kind: 'full' });
|
|
// …nor an If-Range sent for a resource we have no ETag for.
|
|
expect(decideRange({ range: 'bytes=2-5', 'if-range': '"abc"' }, 10, undefined)).toEqual({ kind: 'full' });
|
|
});
|
|
|
|
it('reads the first value of a repeated Range header', () => {
|
|
expect(decideRange({ range: ['bytes=2-5', 'bytes=0-1'] }, 10, '"abc"')).toEqual({
|
|
kind: 'partial',
|
|
start: 2,
|
|
end: 5,
|
|
});
|
|
});
|
|
});
|