Files
VRCX/src/shared/utils/__tests__/compare.test.js
2025-10-15 12:24:14 +11:00

405 lines
14 KiB
JavaScript

import {
compareByCreatedAt,
compareByCreatedAtAscending,
compareByDisplayName,
compareByLastActive,
compareByLastSeen,
compareByLocation,
compareByLocationAt,
compareByMemberCount,
compareByName,
compareByPrivate,
compareByStatus,
compareByUpdatedAt
} from '../compare';
describe('Compare Functions', () => {
describe('compareByName', () => {
test('compares objects by name property', () => {
const a = { name: 'Alice' };
const b = { name: 'Bob' };
expect(compareByName(a, b)).toBeLessThan(0);
expect(compareByName(b, a)).toBeGreaterThan(0);
});
test('returns 0 for equal names', () => {
const a = { name: 'Alice' };
const b = { name: 'Alice' };
expect(compareByName(a, b)).toBe(0);
});
test('handles non-string name properties', () => {
const a = { name: null };
const b = { name: 'Bob' };
expect(compareByName(a, b)).toBe(0);
const c = { name: 123 };
const d = { name: 'Alice' };
expect(compareByName(c, d)).toBe(0);
});
test('handles missing name properties', () => {
const a = {};
const b = { name: 'Bob' };
expect(compareByName(a, b)).toBe(0);
});
test('uses locale-aware comparison', () => {
const a = { name: 'ä' };
const b = { name: 'z' };
expect(typeof compareByName(a, b)).toBe('number');
});
});
describe('compareByCreatedAt', () => {
test('compares objects by created_at in descending order', () => {
const a = { created_at: '2023-01-01' };
const b = { created_at: '2023-01-02' };
expect(compareByCreatedAt(a, b)).toBeGreaterThan(0);
expect(compareByCreatedAt(b, a)).toBeLessThan(0);
});
test('returns 0 for equal created_at', () => {
const a = { created_at: '2023-01-01' };
const b = { created_at: '2023-01-01' };
expect(compareByCreatedAt(a, b)).toBe(0);
});
test('handles non-string created_at properties', () => {
const a = { created_at: null };
const b = { created_at: '2023-01-01' };
expect(compareByCreatedAt(a, b)).toBe(0);
const c = { created_at: 123 };
const d = { created_at: '2023-01-01' };
expect(compareByCreatedAt(c, d)).toBe(0);
});
test('handles case-insensitive comparison', () => {
const a = { created_at: '2023-01-01t12:00:00z' };
const b = { created_at: '2023-01-01T12:00:00Z' };
expect(compareByCreatedAt(a, b)).toBe(0);
});
});
describe('compareByCreatedAtAscending', () => {
test('compares objects by created_at in ascending order', () => {
const a = { created_at: '2023-01-01' };
const b = { created_at: '2023-01-02' };
expect(compareByCreatedAtAscending(a, b)).toBeLessThan(0);
expect(compareByCreatedAtAscending(b, a)).toBeGreaterThan(0);
});
test('returns 0 for equal created_at', () => {
const a = { created_at: '2023-01-01' };
const b = { created_at: '2023-01-01' };
expect(compareByCreatedAtAscending(a, b)).toBe(0);
});
test('handles undefined created_at', () => {
const a = { created_at: undefined };
const b = { created_at: '2023-01-01' };
// undefined comparison with string: both undefined < string and undefined > string are false
// So it falls through to return 0
expect(compareByCreatedAtAscending(a, b)).toBe(0);
});
});
describe('compareByUpdatedAt', () => {
test('compares objects by updated_at in descending order', () => {
const a = { updated_at: '2023-01-01' };
const b = { updated_at: '2023-01-02' };
expect(compareByUpdatedAt(a, b)).toBeGreaterThan(0);
expect(compareByUpdatedAt(b, a)).toBeLessThan(0);
});
test('returns 0 for equal updated_at', () => {
const a = { updated_at: '2023-01-01' };
const b = { updated_at: '2023-01-01' };
expect(compareByUpdatedAt(a, b)).toBe(0);
});
test('handles non-string updated_at properties', () => {
const a = { updated_at: null };
const b = { updated_at: '2023-01-01' };
expect(compareByUpdatedAt(a, b)).toBe(0);
});
test('handles case-insensitive comparison', () => {
const a = { updated_at: '2023-01-01t12:00:00z' };
const b = { updated_at: '2023-01-01T12:00:00Z' };
expect(compareByUpdatedAt(a, b)).toBe(0);
});
});
describe('compareByDisplayName', () => {
test('compares objects by displayName property', () => {
const a = { displayName: 'Alice Display' };
const b = { displayName: 'Bob Display' };
expect(compareByDisplayName(a, b)).toBeLessThan(0);
expect(compareByDisplayName(b, a)).toBeGreaterThan(0);
});
test('returns 0 for equal displayNames', () => {
const a = { displayName: 'Alice Display' };
const b = { displayName: 'Alice Display' };
expect(compareByDisplayName(a, b)).toBe(0);
});
test('handles non-string displayName properties', () => {
const a = { displayName: null };
const b = { displayName: 'Bob Display' };
expect(compareByDisplayName(a, b)).toBe(0);
const c = { displayName: 123 };
const d = { displayName: 'Alice Display' };
expect(compareByDisplayName(c, d)).toBe(0);
});
});
describe('compareByMemberCount', () => {
test('compares objects by memberCount property', () => {
const a = { memberCount: 5 };
const b = { memberCount: 10 };
expect(compareByMemberCount(a, b)).toBe(-5);
expect(compareByMemberCount(b, a)).toBe(5);
});
test('returns 0 for equal memberCounts', () => {
const a = { memberCount: 5 };
const b = { memberCount: 5 };
expect(compareByMemberCount(a, b)).toBe(0);
});
test('handles non-number memberCount properties', () => {
const a = { memberCount: 'invalid' };
const b = { memberCount: 10 };
expect(compareByMemberCount(a, b)).toBe(0);
const c = { memberCount: null };
const d = { memberCount: 5 };
expect(compareByMemberCount(c, d)).toBe(0);
});
test('handles negative member counts', () => {
const a = { memberCount: -5 };
const b = { memberCount: 10 };
expect(compareByMemberCount(a, b)).toBe(-15);
});
});
describe('compareByPrivate', () => {
test('prioritizes non-private locations', () => {
const a = { ref: { location: 'public' } };
const b = { ref: { location: 'private' } };
expect(compareByPrivate(a, b)).toBe(-1);
expect(compareByPrivate(b, a)).toBe(1);
});
test('returns 0 when both are private', () => {
const a = { ref: { location: 'private' } };
const b = { ref: { location: 'private' } };
expect(compareByPrivate(a, b)).toBe(0);
});
test('returns 0 when both are non-private', () => {
const a = { ref: { location: 'public' } };
const b = { ref: { location: 'friends' } };
expect(compareByPrivate(a, b)).toBe(0);
});
test('handles undefined ref properties', () => {
const a = { ref: undefined };
const b = { ref: { location: 'private' } };
expect(compareByPrivate(a, b)).toBe(0);
const c = {};
const d = { ref: { location: 'public' } };
expect(compareByPrivate(c, d)).toBe(0);
});
});
describe('compareByStatus', () => {
test('handles offline users', () => {
const a = { ref: { state: 'offline', status: 'active' } };
const b = { ref: { state: 'online', status: 'busy' } };
expect(compareByStatus(a, b)).toBe(1);
});
test('returns 0 for same status', () => {
const a = { ref: { status: 'active' } };
const b = { ref: { status: 'active' } };
expect(compareByStatus(a, b)).toBe(0);
});
test('handles undefined ref properties', () => {
const a = { ref: undefined };
const b = { ref: { status: 'active' } };
expect(compareByStatus(a, b)).toBe(0);
});
});
describe('compareByLastActive', () => {
test('compares online users by $online_for', () => {
const a = {
state: 'online',
ref: { $online_for: 100 }
};
const b = {
state: 'online',
ref: { $online_for: 200 }
};
expect(compareByLastActive(a, b)).toBe(1);
});
test('falls back to last_activity for non-online users', () => {
const a = {
state: 'offline',
ref: { last_activity: '2023-01-01' }
};
const b = {
state: 'offline',
ref: { last_activity: '2023-01-02' }
};
expect(compareByLastActive(a, b)).toBe(1);
});
test('handles undefined ref properties', () => {
const a = { state: 'online', ref: undefined };
const b = { state: 'online', ref: { $online_for: 100 } };
expect(compareByLastActive(a, b)).toBe(0);
});
});
describe('compareByLastSeen', () => {
test('compares by $lastSeen field', () => {
const a = { ref: { $lastSeen: '2023-01-01' } };
const b = { ref: { $lastSeen: '2023-01-02' } };
expect(compareByLastSeen(a, b)).toBe(1);
});
test('handles empty string as longest active', () => {
const a = { ref: { $lastSeen: '' } };
const b = { ref: { $lastSeen: '2023-01-01' } };
// '' < '2023-01-01' is true, so first condition matches and returns 1
expect(compareByLastSeen(a, b)).toBe(1);
});
test('handles undefined ref properties', () => {
const a = { ref: undefined };
const b = { ref: { $lastSeen: '2023-01-01' } };
expect(compareByLastSeen(a, b)).toBe(0);
});
});
describe('compareByLocationAt', () => {
test('handles traveling status', () => {
const a = { location: 'traveling', $location_at: 100 };
const b = { location: 'public', $location_at: 200 };
expect(compareByLocationAt(a, b)).toBe(1);
expect(compareByLocationAt(b, a)).toBe(-1);
});
test('returns 0 when both are traveling', () => {
const a = { location: 'traveling', $location_at: 100 };
const b = { location: 'traveling', $location_at: 200 };
expect(compareByLocationAt(a, b)).toBe(0);
});
test('compares by $location_at for non-traveling locations', () => {
const a = { location: 'public', $location_at: 100 };
const b = { location: 'friends', $location_at: 200 };
expect(compareByLocationAt(a, b)).toBe(-1);
expect(compareByLocationAt(b, a)).toBe(1);
});
test('returns 0 for equal $location_at', () => {
const a = { location: 'public', $location_at: 100 };
const b = { location: 'friends', $location_at: 100 };
expect(compareByLocationAt(a, b)).toBe(0);
});
});
describe('compareByLocation', () => {
test('compares online users by location', () => {
const a = {
state: 'online',
ref: { location: 'public' }
};
const b = {
state: 'online',
ref: { location: 'friends' }
};
expect(compareByLocation(a, b)).toBeGreaterThan(0);
});
test('returns 0 for non-online users', () => {
const a = {
state: 'offline',
ref: { location: 'public' }
};
const b = {
state: 'offline',
ref: { location: 'friends' }
};
expect(compareByLocation(a, b)).toBe(0);
});
test('returns 0 when one user is not online', () => {
const a = {
state: 'online',
ref: { location: 'public' }
};
const b = {
state: 'offline',
ref: { location: 'friends' }
};
expect(compareByLocation(a, b)).toBe(0);
});
test('handles undefined ref properties', () => {
const a = { state: 'online', ref: undefined };
const b = { state: 'online', ref: { location: 'public' } };
expect(compareByLocation(a, b)).toBe(0);
});
test('uses locale-aware comparison', () => {
const a = {
state: 'online',
ref: { location: 'ä' }
};
const b = {
state: 'online',
ref: { location: 'z' }
};
expect(typeof compareByLocation(a, b)).toBe('number');
});
});
describe('edge cases and boundary conditions', () => {
test('handles null objects', () => {
// compareByName doesn't handle null objects - it will throw
expect(() => compareByName(null, { name: 'test' })).toThrow();
expect(() => compareByName({ name: 'test' }, null)).toThrow();
});
test('handles empty objects', () => {
expect(compareByName({}, {})).toBe(0);
expect(compareByDisplayName({}, {})).toBe(0);
expect(compareByMemberCount({}, {})).toBe(0);
});
test('handles mixed valid and invalid data', () => {
const valid = { memberCount: 5 };
const invalid = { memberCount: 'not a number' };
expect(compareByMemberCount(valid, invalid)).toBe(0);
});
test('handles zero values', () => {
const a = { memberCount: 0 };
const b = { memberCount: 5 };
expect(compareByMemberCount(a, b)).toBe(-5);
});
});
});