From 555bdd87f71cba6b47d88de05521d4e96aa46c8f Mon Sep 17 00:00:00 2001 From: Script Raccoon Date: Sun, 4 Oct 2026 17:12:24 +0200 Subject: [PATCH 1/2] remove dates from structure history --- database/scripts/seed.ts | 10 +- shared/structure.history.json | 436 +++++++++++++++++----------------- src/routes/+page.server.ts | 7 +- 3 files changed, 226 insertions(+), 227 deletions(-) diff --git a/database/scripts/seed.ts b/database/scripts/seed.ts index f32f894a5..ba09876f8 100644 --- a/database/scripts/seed.ts +++ b/database/scripts/seed.ts @@ -20,7 +20,7 @@ const db = get_client({ readonly: false }) const data_folder = path.resolve('database', 'data') const structure_history_file = path.resolve('shared', 'structure.history.json') -const structure_history: Record = JSON.parse( +const structure_history: string[] = JSON.parse( fs.readFileSync(structure_history_file, 'utf8') ) @@ -338,13 +338,13 @@ function seed_structures({ type, folder }: { type: StructureType; folder: string } /** - * Adds the structure to the history in case it is new. + * Appends the structure to the history in case it is new. + * The history lists structure ids in the order they were added. */ function record_structure_addition(id: string) { - if (structure_history[id]) return + if (structure_history.includes(id)) return - const date = new Date().toLocaleDateString('en-CA') - structure_history[id] = date + structure_history.push(id) structure_history_changed = true } diff --git a/shared/structure.history.json b/shared/structure.history.json index 8e53ce898..dbe579f93 100644 --- a/shared/structure.history.json +++ b/shared/structure.history.json @@ -1,218 +1,218 @@ -{ - "0": "2026-03-26", - "1": "2026-03-26", - "2": "2026-03-26", - "Ab": "2026-03-26", - "Alg(R)": "2026-03-26", - "B": "2026-03-26", - "BG": "2026-03-26", - "BN": "2026-03-26", - "BOn": "2026-03-26", - "Ban": "2026-03-26", - "CAlg(R)": "2026-03-26", - "CMon": "2026-03-26", - "CRing": "2026-03-26", - "Cat": "2026-03-26", - "FI": "2026-03-26", - "FS": "2026-03-26", - "FinAb": "2026-03-26", - "FinOrd": "2026-03-26", - "FinSet": "2026-03-26", - "Fld": "2026-03-26", - "FreeAb": "2026-03-26", - "Grp": "2026-03-26", - "M-Set": "2026-03-26", - "Man": "2026-03-26", - "Meas": "2026-03-26", - "Met": "2026-03-26", - "Met_c": "2026-03-26", - "Met_oo": "2026-03-26", - "Mon": "2026-03-26", - "N": "2026-03-26", - "On": "2026-03-26", - "Pos": "2026-03-26", - "R-Mod": "2026-03-26", - "R-Mod_div": "2026-03-26", - "Rel": "2026-03-26", - "Ring": "2026-03-26", - "Rng": "2026-03-26", - "Set": "2026-03-26", - "Set_op": "2026-03-26", - "Setne": "2026-03-26", - "Sh(X)": "2026-03-26", - "Sh(X,Ab)": "2026-03-26", - "Sp": "2026-03-26", - "Top": "2026-03-26", - "Vect": "2026-03-26", - "Z": "2026-03-26", - "real_interval": "2026-03-26", - "sSet": "2026-03-26", - "walking_composable_pair": "2026-03-26", - "walking_isomorphism": "2026-03-26", - "walking_morphism": "2026-03-26", - "walking_pair": "2026-03-26", - "Ab_fg": "2026-03-27", - "BG_f": "2026-03-27", - "N_oo": "2026-03-27", - "Set_*": "2026-03-27", - "Z_div": "2026-03-27", - "Haus": "2026-03-28", - "abelianization": "2026-03-31", - "forget_vector": "2026-03-31", - "free_group": "2026-03-31", - "id_Set": "2026-03-31", - "power_set_contravariant": "2026-03-31", - "power_set_covariant": "2026-03-31", - "walking_commutative_square": "2026-04-01", - "walking_fork": "2026-04-01", - "walking_span": "2026-04-01", - "SetxSet": "2026-04-02", - "Top_*": "2026-04-03", - "walking_idempotent": "2026-04-03", - "FinGrp": "2026-04-07", - "Delta": "2026-04-08", - "BG_c": "2026-04-16", - "PMet": "2026-04-18", - "Set_c": "2026-04-20", - "walking_splitting": "2026-04-25", - "walking_coreflexive_pair": "2026-04-30", - "J2": "2026-05-01", - "TorsFreeAb": "2026-05-01", - "TorsAb": "2026-05-02", - "CompHaus": "2026-05-04", - "SemiGrp": "2026-05-05", - "Grp_c": "2026-05-12", - "forget_group": "2026-05-25", - "forget_ring": "2026-05-25", - "forget_topology": "2026-05-25", - "Top_op": "2026-05-26", - "continuous_functions": "2026-05-26", - "discrete_topology": "2026-05-26", - "forget_abelian": "2026-05-26", - "countable_copower_sets": "2026-05-27", - "diagonal_sets": "2026-05-27", - "doubling_sets": "2026-05-27", - "sequences_sets": "2026-05-27", - "squaring_sets": "2026-05-27", - "modulo-p": "2026-05-28", - "p-torsion": "2026-05-28", - "torsion": "2026-05-28", - "enveloping_group": "2026-05-30", - "forget_inverses": "2026-05-30", - "group_units": "2026-05-30", - "LRS_R": "2026-06-04", - "Sch_R": "2026-06-04", - "forget_addition": "2026-06-07", - "monoid_ring": "2026-06-07", - "FiltVect": "2026-06-13", - "FinVect": "2026-06-13", - "BG_u": "2026-06-14", - "FinVect_c": "2026-06-14", - "FinVect_f": "2026-06-14", - "FinVect_u": "2026-06-14", - "forget_group_pointed": "2026-06-18", - "indiscrete_topology": "2026-06-18", - "pi_0": "2026-06-18", - "binary_coproduct_sets": "2026-06-25", - "binary_product_sets": "2026-06-25", - "trivial_groups": "2026-06-26", - "trivial_sets": "2026-06-26", - "pi_1": "2026-06-27", - "opposite_category": "2026-07-02", - "opposite_monoid": "2026-07-02", - "walking_isomorphism_object_inclusion": "2026-07-02", - "Set_arrow": "2026-07-03", - "Mono": "2026-07-04", - "empty-map": "2026-07-04", - "id_X": "2026-07-04", - "integer-rational-embedding": "2026-07-04", - "terminal-map": "2026-07-04", - "PreOrd": "2026-07-12", - "fork-handle": "2026-07-15", - "universal-split-epi": "2026-07-15", - "A3-S3-embedding": "2026-07-16", - "baer-specker-relations": "2026-07-16", - "id_G": "2026-07-16", - "multiply-2": "2026-07-16", - "reduction-mod-p": "2026-07-16", - "universal-morphism": "2026-07-17", - "walking-idempotent-presentation": "2026-07-17", - "forget_torsion_free": "2026-07-22", - "inclusion_ordinals": "2026-07-22", - "simple_group_probing": "2026-07-22", - "span_endpoints_inclusion": "2026-07-22", - "trivial_BG": "2026-07-22", - "trivial_Idem": "2026-07-22", - "nerve": "2026-07-23", - "rational_product": "2026-07-23", - "stone-cech-compactification": "2026-07-23", - "walking_morphism_representation": "2026-07-23", - "forget_finite": "2026-07-24", - "morphism_endpoints_inclusion": "2026-07-24", - "ring_idempotents": "2026-07-24", - "brauer_group": "2026-07-25", - "empty_sets": "2026-07-26", - "forget_commutative": "2026-07-26", - "forget_finite_abelian_group": "2026-07-26", - "forget_finite_group": "2026-07-26", - "forget_hausdorff": "2026-07-26", - "forget_torsion": "2026-07-26", - "Set_ff": "2026-07-28", - "Unif": "2026-08-01", - "extremal-not-strong-example": "2026-08-05", - "forked_commutative_square": "2026-08-05", - "1_tensor": "2026-08-08", - "Ab_fg_tensor": "2026-08-08", - "Ab_tensor": "2026-08-08", - "Cat_cartesian": "2026-08-08", - "FinVect_tensor": "2026-08-08", - "N+": "2026-08-08", - "R-Mod_tensor": "2026-08-08", - "R-Mod_tensor_af": "2026-08-08", - "R-Mod_tensor_naf": "2026-08-08", - "Set_cartesian": "2026-08-08", - "Set_cocartesian": "2026-08-08", - "Top_cartesian": "2026-08-08", - "inclusion_positive_numbers": "2026-08-09", - "R-Mod_non_ss": "2026-08-10", - "grAb": "2026-08-11", - "grMod_G(R)": "2026-08-11", - "Ch(Ab)": "2026-08-12", - "SeqAb": "2026-08-12", - "cocompletion-discrete-pair-join": "2026-08-19", - "FreeAb_fg": "2026-09-02", - "Proj_fg(Re)": "2026-09-03", - "FinSet_even": "2026-09-04", - "FinSet_power_3": "2026-09-04", - "Euclid_coprod": "2026-09-05", - "FinSet_odd": "2026-09-05", - "Fld_0": "2026-09-05", - "Pos_noiso": "2026-09-05", - "Free_fg(ZxZ)": "2026-09-06", - "Set_disc": "2026-09-07", - "Ab_family": "2026-09-07", - "TransSeqAb": "2026-09-08", - "Vect_c": "2026-09-09", - "Vect_large": "2026-09-09", - "Vect_family_mostly_0": "2026-09-11", - "Set_family_mostly_1": "2026-09-12", - "Set_family_mostly_0": "2026-09-12", - "Set_family": "2026-09-13", - "Quiv": "2026-09-14", - "Bin": "2026-09-14", - "Set_indisc": "2026-09-24", - "SetxSet_0_fin": "2026-09-26", - "Quiv_fc": "2026-09-26", - "SeqSet": "2026-09-26", - "SeqSet_conn": "2026-09-26", - "Z-Set": "2026-09-26", - "Z-FinSet": "2026-09-26", - "F(I)-Set": "2026-09-26", - "Bin_irr": "2026-09-29", - "Bin_symm_irr": "2026-09-29", - "Bin_refl": "2026-09-30", - "Bin_symm": "2026-10-01", - "Bin_symm_refl": "2026-10-01", - "Set_bij": "2026-10-02", - "Set_family_bij": "2026-10-02" -} +[ + "0", + "1", + "2", + "Ab", + "Alg(R)", + "B", + "BG", + "BN", + "BOn", + "Ban", + "CAlg(R)", + "CMon", + "CRing", + "Cat", + "FI", + "FS", + "FinAb", + "FinOrd", + "FinSet", + "Fld", + "FreeAb", + "Grp", + "M-Set", + "Man", + "Meas", + "Met", + "Met_c", + "Met_oo", + "Mon", + "N", + "On", + "Pos", + "R-Mod", + "R-Mod_div", + "Rel", + "Ring", + "Rng", + "Set", + "Set_op", + "Setne", + "Sh(X)", + "Sh(X,Ab)", + "Sp", + "Top", + "Vect", + "Z", + "real_interval", + "sSet", + "walking_composable_pair", + "walking_isomorphism", + "walking_morphism", + "walking_pair", + "Ab_fg", + "BG_f", + "N_oo", + "Set_*", + "Z_div", + "Haus", + "abelianization", + "forget_vector", + "free_group", + "id_Set", + "power_set_contravariant", + "power_set_covariant", + "walking_commutative_square", + "walking_fork", + "walking_span", + "SetxSet", + "Top_*", + "walking_idempotent", + "FinGrp", + "Delta", + "BG_c", + "PMet", + "Set_c", + "walking_splitting", + "walking_coreflexive_pair", + "J2", + "TorsFreeAb", + "TorsAb", + "CompHaus", + "SemiGrp", + "Grp_c", + "forget_group", + "forget_ring", + "forget_topology", + "Top_op", + "continuous_functions", + "discrete_topology", + "forget_abelian", + "countable_copower_sets", + "diagonal_sets", + "doubling_sets", + "sequences_sets", + "squaring_sets", + "modulo-p", + "p-torsion", + "torsion", + "enveloping_group", + "forget_inverses", + "group_units", + "LRS_R", + "Sch_R", + "forget_addition", + "monoid_ring", + "FiltVect", + "FinVect", + "BG_u", + "FinVect_c", + "FinVect_f", + "FinVect_u", + "forget_group_pointed", + "indiscrete_topology", + "pi_0", + "binary_coproduct_sets", + "binary_product_sets", + "trivial_groups", + "trivial_sets", + "pi_1", + "opposite_category", + "opposite_monoid", + "walking_isomorphism_object_inclusion", + "Set_arrow", + "Mono", + "empty-map", + "id_X", + "integer-rational-embedding", + "terminal-map", + "PreOrd", + "fork-handle", + "universal-split-epi", + "A3-S3-embedding", + "baer-specker-relations", + "id_G", + "multiply-2", + "reduction-mod-p", + "universal-morphism", + "walking-idempotent-presentation", + "forget_torsion_free", + "inclusion_ordinals", + "simple_group_probing", + "span_endpoints_inclusion", + "trivial_BG", + "trivial_Idem", + "nerve", + "rational_product", + "stone-cech-compactification", + "walking_morphism_representation", + "forget_finite", + "morphism_endpoints_inclusion", + "ring_idempotents", + "brauer_group", + "empty_sets", + "forget_commutative", + "forget_finite_abelian_group", + "forget_finite_group", + "forget_hausdorff", + "forget_torsion", + "Set_ff", + "Unif", + "extremal-not-strong-example", + "forked_commutative_square", + "1_tensor", + "Ab_fg_tensor", + "Ab_tensor", + "Cat_cartesian", + "FinVect_tensor", + "N+", + "R-Mod_tensor", + "R-Mod_tensor_af", + "R-Mod_tensor_naf", + "Set_cartesian", + "Set_cocartesian", + "Top_cartesian", + "inclusion_positive_numbers", + "R-Mod_non_ss", + "grAb", + "grMod_G(R)", + "Ch(Ab)", + "SeqAb", + "cocompletion-discrete-pair-join", + "FreeAb_fg", + "Proj_fg(Re)", + "FinSet_even", + "FinSet_power_3", + "Euclid_coprod", + "FinSet_odd", + "Fld_0", + "Pos_noiso", + "Free_fg(ZxZ)", + "Set_disc", + "Ab_family", + "TransSeqAb", + "Vect_c", + "Vect_large", + "Vect_family_mostly_0", + "Set_family_mostly_1", + "Set_family_mostly_0", + "Set_family", + "Quiv", + "Bin", + "Set_indisc", + "SetxSet_0_fin", + "Quiv_fc", + "SeqSet", + "SeqSet_conn", + "Z-Set", + "Z-FinSet", + "F(I)-Set", + "Bin_irr", + "Bin_symm_irr", + "Bin_refl", + "Bin_symm", + "Bin_symm_refl", + "Set_bij", + "Set_family_bij" +] diff --git a/src/routes/+page.server.ts b/src/routes/+page.server.ts index 0df7537db..96cda4064 100644 --- a/src/routes/+page.server.ts +++ b/src/routes/+page.server.ts @@ -91,10 +91,9 @@ export const load = () => { FinAb: example_structures_db[1] }) - const recent_structures_ids = Object.entries(structure_history) - .sort((a, b) => b[1].localeCompare(a[1])) - .slice(0, NUMBER_RECENT_STRUCTURES) - .map((a) => a[0]) + const recent_structures_ids = structure_history + .slice(-NUMBER_RECENT_STRUCTURES) + .reverse() const recent_structures = db .prepare( From b647c5618763b2bde82447ddfbcf21077a5ba4c3 Mon Sep 17 00:00:00 2001 From: Script Raccoon Date: Sun, 4 Oct 2026 17:27:03 +0200 Subject: [PATCH 2/2] add tests for structure history --- database/scripts/test.ts | 24 ++++++++++++++++++++++++ tests/homepage.spec.ts | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/database/scripts/test.ts b/database/scripts/test.ts index d19cb176e..db768fb62 100644 --- a/database/scripts/test.ts +++ b/database/scripts/test.ts @@ -12,6 +12,7 @@ import decided_categories from './expected-data/decided-categories.json' import decided_functors from './expected-data/decided-functors.json' import decided_morphisms from './expected-data/decided-morphisms.json' import decided_symmetric_monoidal_categories from './expected-data/decided-symmetric-monoidal-categories.json' +import structure_history from '$shared/structure.history.json' import { capitalize, devlog, remove_underscores } from '$shared/utils' import { get_client } from '$shared/db' import { STRUCTURE_TYPES, type StructureType, PLURALS } from '$shared/config' @@ -33,6 +34,9 @@ function execute_tests() { devlog('\n--- Test link targets ---') check_link_targets_exist() + devlog('\n--- Test structure history ---') + test_structure_history_ids_exist() + devlog('\n--- Test categories ---') test_mutual_structure_duals('category') @@ -158,6 +162,26 @@ function test_mutual_property_duals(type: StructureType) { devlog(`✅ ${capitalize(remove_underscores(type))} properties are mutually dual`) } +/** + * Tests that every ID in the structure history refers to an existing structure. + * If this test fails, a structure has probably been renamed or deleted. + */ +function test_structure_history_ids_exist() { + const structure_ids = new Set( + db.prepare(`SELECT id FROM structures`).pluck().all() + ) + + const unknown_ids = structure_history.filter((id) => !structure_ids.has(id)) + + if (unknown_ids.length > 0) { + throw new Error( + `❌ Found unknown structure IDs in structure.history.json:\n${unknown_ids.join(', ')}` + ) + } + + devlog('✅ All structure IDs in the history exist') +} + /** * Tests that for a specified list of structures all properties have * been decided. If this test fails, property assignments or implications are missing. diff --git a/tests/homepage.spec.ts b/tests/homepage.spec.ts index ded102dec..1feab1416 100644 --- a/tests/homepage.spec.ts +++ b/tests/homepage.spec.ts @@ -142,3 +142,35 @@ test('user sees five recently added structures', async ({ page }) => { const items = section.locator('li') await expect(items).toHaveCount(5) }) + +test('user can see recently added structures', async ({ page }) => { + await page.goto('/', { waitUntil: 'networkidle' }) + + const section = page.locator('section', { + has: page.getByRole('heading', { name: 'Recently added structures' }) + }) + + const items = section.locator('li') + await expect(items).toHaveCount(5) + + const button = section.getByRole('button', { name: 'Show more' }) + await button.click() + + await expect.poll(() => items.count()).toBeGreaterThan(5) + await expect(button).toBeHidden() +}) + +test('user can navigate to a recently added structure', async ({ page }) => { + await page.goto('/') + + const section = page.locator('section', { + has: page.getByRole('heading', { name: 'Recently added structures' }) + }) + + const link = section.getByRole('link').first() + const name = (await link.textContent())!.trim() + + await link.click() + + await expect(page.getByRole('heading', { name, exact: true })).toBeVisible() +})