src/flat-html-generator.download-links.test.lua
1#!/usr/bin/env luajit
2
3-- Guards the download offer at the top of every similar/difference page: the
4-- link must name a file that was actually written, at an address that resolves
5-- from where the page lives.
6--
7-- Why this test exists. The offer shipped with three separate faults at once,
8-- any one of which alone makes it dead:
9-- 1. it built the filename from the numeric poem index ("4355.txt") while the
10-- writer names exports by category-prefixed id ("fediverse-4355.txt");
11-- 2. it prefixed the directory ("similar/...") on a page already inside
12-- similar/, so browsers resolved it to similar/similar/...;
13-- 3. it advertised an .html archive that the shipped config never generates.
14-- None of these are visible in the generated HTML unless you compare it against
15-- what is on disk, which is why they survived.
16--
17-- Run directly: luajit src/flat-html-generator.download-links.test.lua
18
19-- {{{ local function setup_path()
20local function setup_path()
21 local this = debug.getinfo(1, "S").source:sub(2)
22 local src_dir = this:match("(.*/)") or "./"
23 local project_dir = src_dir:gsub("src/$", "")
24 package.path = src_dir .. "?.lua;" .. project_dir .. "libs/?.lua;" .. package.path
25 return project_dir
26end
27-- }}}
28
29setup_path()
30local generator = require("flat-html-generator")
31
32local passed, failed = 0, 0
33
34-- {{{ local function check()
35local function check(name, condition, detail)
36 if condition then
37 passed = passed + 1
38 print(" ok " .. name)
39 else
40 failed = failed + 1
41 print(" FAIL " .. name .. (detail and (" -- " .. detail) or ""))
42 end
43end
44-- }}}
45
46-- {{{ local function make_poem()
47-- category and id are the two fields the export filename is built from; they
48-- are deliberately different from poem_index here, because using poem_index by
49-- mistake is exactly the bug under test.
50local function make_poem(poem_index, category, id)
51 return {
52 poem_index = poem_index,
53 id = id or poem_index,
54 category = category or "messages",
55 title = "Poem " .. poem_index,
56 content = "a line of text for poem " .. poem_index,
57 filename = (category or "messages") .. "/" .. poem_index,
58 }
59end
60-- }}}
61
62-- {{{ local function make_ranking()
63local function make_ranking(indices)
64 local ranking = {}
65 for _, index in ipairs(indices) do
66 table.insert(ranking, { id = index, poem = make_poem(index), similarity = 0.5 })
67 end
68 return ranking
69end
70-- }}}
71
72-- {{{ local function render()
73-- Builds one similar page and returns its HTML. total_pages drives the plural.
74local function render(anchor, total_pages)
75 return generator.generate_paginated_poem_page_html(
76 anchor, make_ranking({ 120, 3549 }), "similar", anchor.poem_index,
77 1, total_pages or 1, 400, nil, false)
78end
79-- }}}
80
81-- {{{ local function download_hrefs()
82-- Every href that appears inside the download offer line.
83local function download_hrefs(html)
84 local line = html:match("Download[^\n]*")
85 if not line then return {} end
86 local hrefs = {}
87 for href in line:gmatch('href="([^"]+)"') do table.insert(hrefs, href) end
88 return hrefs
89end
90-- }}}
91
92-- The anchor: poem_index 4355 but a category-prefixed export name of
93-- "fediverse-0777", so a link built from the index cannot accidentally pass.
94local ANCHOR = make_poem(4355, "fediverse", 777)
95
96-- {{{ Case: the link names the file the writer actually produces
97do
98 local hrefs = download_hrefs(render(ANCHOR))
99 check("a download link is offered", #hrefs > 0)
100 check("link uses the category-prefixed export name",
101 hrefs[1] == "fediverse-0777.txt",
102 "got " .. tostring(hrefs[1]))
103 check("link does not use the numeric poem index",
104 hrefs[1] ~= "4355.txt" and hrefs[1] ~= "similar/4355.txt",
105 "got " .. tostring(hrefs[1]))
106end
107-- }}}
108
109-- {{{ Case: the link resolves from inside the page's own directory
110-- The page lives at similar/NNNN-01.html and the export at similar/<id>.txt, so
111-- they are siblings. Any directory component makes the browser look one level
112-- too deep.
113do
114 local hrefs = download_hrefs(render(ANCHOR))
115 check("link carries no directory component",
116 hrefs[1] and not hrefs[1]:find("/"),
117 "got " .. tostring(hrefs[1]))
118 check("link is not double-nested",
119 hrefs[1] ~= "similar/fediverse-0777.txt",
120 "got " .. tostring(hrefs[1]))
121end
122-- }}}
123
124-- {{{ Case: no link is offered for an export that is not generated
125-- generate_html_archives ships off, so the .html archive does not exist. A link
126-- to it is dead on arrival.
127do
128 local hrefs = download_hrefs(render(ANCHOR))
129 local archive_offered = false
130 for _, href in ipairs(hrefs) do
131 if href:find("%-archive%.html$") then archive_offered = true end
132 end
133 check("archive link is withheld while archives are disabled",
134 not archive_offered,
135 "an -archive.html link was offered")
136end
137-- }}}
138
139-- {{{ Case: the label counts the pages in front of the reader
140do
141 check("one page reads as singular",
142 render(ANCHOR, 1):find("Download this page:") ~= nil)
143 check("several pages read as plural",
144 render(ANCHOR, 4):find("Download these pages:") ~= nil)
145 check("singular wording is not used for several pages",
146 render(ANCHOR, 4):find("Download this page:") == nil)
147end
148-- }}}
149
150-- {{{ Case: a different anchor gets a different filename
151-- Guards against the name being derived from anything page-global rather than
152-- from the anchor poem.
153do
154 local other = make_poem(9, "notes", 12)
155 local hrefs = download_hrefs(render(other))
156 check("second anchor gets its own export name",
157 hrefs[1] == "notes-0012.txt",
158 "got " .. tostring(hrefs[1]))
159end
160-- }}}
161
162print(string.format("\n%d passed, %d failed", passed, failed))
163os.exit(failed == 0 and 0 or 1)
164