repos / pico

pico services mono repo
git clone https://github.com/picosh/pico.git

pico / pkg / apps / pgs
Antonio Mika  ·  2025-03-12

calc_route_test.go

  1package pgs
  2
  3import (
  4	"testing"
  5
  6	"github.com/google/go-cmp/cmp"
  7)
  8
  9type RouteFixture struct {
 10	Name     string
 11	Actual   []*HttpReply
 12	Expected []*HttpReply
 13}
 14
 15func TestCalcRoutes(t *testing.T) {
 16	fixtures := []RouteFixture{
 17		{
 18			Name:   "basic-index",
 19			Actual: calcRoutes("test", "/index.html", []*RedirectRule{}),
 20			Expected: []*HttpReply{
 21				{Filepath: "test/index.html", Status: 200},
 22				{Filepath: "/index.html/", Status: 301},
 23				{Filepath: "test/404.html", Status: 404},
 24			},
 25		},
 26		{
 27			Name:   "basic-txt",
 28			Actual: calcRoutes("test", "/index.txt", []*RedirectRule{}),
 29			Expected: []*HttpReply{
 30				{Filepath: "test/index.txt", Status: 200},
 31				{Filepath: "/index.txt/", Status: 301},
 32				{Filepath: "test/404.html", Status: 404},
 33			},
 34		},
 35		{
 36			Name:   "basic-named",
 37			Actual: calcRoutes("test", "/wow.html", []*RedirectRule{}),
 38			Expected: []*HttpReply{
 39				{Filepath: "test/wow.html", Status: 200},
 40				{Filepath: "/wow.html/", Status: 301},
 41				{Filepath: "test/404.html", Status: 404},
 42			},
 43		},
 44		{
 45			Name:   "subdirectory-index",
 46			Actual: calcRoutes("test", "/nice/index.html", []*RedirectRule{}),
 47			Expected: []*HttpReply{
 48				{Filepath: "test/nice/index.html", Status: 200},
 49				{Filepath: "/nice/index.html/", Status: 301},
 50				{Filepath: "test/404.html", Status: 404},
 51			},
 52		},
 53		{
 54			Name:   "subdirectory-named",
 55			Actual: calcRoutes("test", "/nice/wow.html", []*RedirectRule{}),
 56			Expected: []*HttpReply{
 57				{Filepath: "test/nice/wow.html", Status: 200},
 58				{Filepath: "/nice/wow.html/", Status: 301},
 59				{Filepath: "test/404.html", Status: 404},
 60			},
 61		},
 62		{
 63			Name:   "subdirectory-bare",
 64			Actual: calcRoutes("test", "/nice/", []*RedirectRule{}),
 65			Expected: []*HttpReply{
 66				{Filepath: "test/nice/index.html", Status: 200},
 67				{Filepath: "test/404.html", Status: 404},
 68			},
 69		},
 70		{
 71			Name:   "trailing-slash",
 72			Actual: calcRoutes("test", "/folder", []*RedirectRule{}),
 73			Expected: []*HttpReply{
 74				{Filepath: "test/folder", Status: 200},
 75				{Filepath: "test/folder.html", Status: 200},
 76				{Filepath: "/folder/", Status: 301},
 77				{Filepath: "test/404.html", Status: 404},
 78			},
 79		},
 80		{
 81			Name: "spa",
 82			Actual: calcRoutes("test", "/nice.html", []*RedirectRule{
 83				{
 84					From:   "/*",
 85					To:     "/index.html",
 86					Status: 200,
 87				},
 88			}),
 89			Expected: []*HttpReply{
 90				{Filepath: "test/nice.html", Status: 200},
 91				{Filepath: "test/index.html", Status: 200},
 92				{Filepath: "/index.html/", Status: 301},
 93				{Filepath: "test/404.html", Status: 404},
 94			},
 95		},
 96		{
 97			Name:   "xml",
 98			Actual: calcRoutes("test", "/index.xml", []*RedirectRule{}),
 99			Expected: []*HttpReply{
100				{Filepath: "test/index.xml", Status: 200},
101				{Filepath: "/index.xml/", Status: 301},
102				{Filepath: "test/404.html", Status: 404},
103			},
104		},
105		{
106			Name: "redirect-rule",
107			Actual: calcRoutes(
108				"test",
109				"/wow",
110				[]*RedirectRule{
111					{
112						From:   "/wow",
113						To:     "/index.html",
114						Status: 301,
115					},
116				},
117			),
118			Expected: []*HttpReply{
119				{Filepath: "test/wow", Status: 200},
120				{Filepath: "test/wow.html", Status: 200},
121				{Filepath: "/index.html", Status: 301},
122				{Filepath: "/wow/", Status: 301},
123				{Filepath: "test/404.html", Status: 404},
124			},
125		},
126		{
127			Name: "redirect-to-pico",
128			Actual: calcRoutes(
129				"test",
130				"/tester1",
131				[]*RedirectRule{
132					{
133						From:   "/tester1",
134						To:     "https://pico.sh",
135						Status: 301,
136					},
137				},
138			),
139			Expected: []*HttpReply{
140				{Filepath: "test/tester1", Status: 200},
141				{Filepath: "test/tester1.html", Status: 200},
142				{Filepath: "https://pico.sh", Status: 301},
143			},
144		},
145		{
146			Name: "root",
147			Actual: calcRoutes(
148				"test",
149				"",
150				[]*RedirectRule{},
151			),
152			Expected: []*HttpReply{
153				{Filepath: "test/index.html", Status: 200},
154				{Filepath: "test/404.html", Status: 404},
155			},
156		},
157		{
158			Name: "redirect-to-root",
159			Actual: calcRoutes(
160				"test",
161				"/wow/",
162				[]*RedirectRule{
163					{
164						From:   "/wow/",
165						To:     "/",
166						Status: 301,
167					},
168				},
169			),
170			Expected: []*HttpReply{
171				{Filepath: "test/wow/index.html", Status: 200},
172				{Filepath: "/", Status: 301},
173				{Filepath: "test/404.html", Status: 404},
174			},
175		},
176		{
177			Name: "force",
178			Actual: calcRoutes(
179				"test",
180				"/wow",
181				[]*RedirectRule{
182					{
183						From:   "/wow",
184						To:     "/",
185						Status: 301,
186						Force:  true,
187					},
188				},
189			),
190			Expected: []*HttpReply{
191				{Filepath: "/", Status: 301},
192				{Filepath: "/wow/", Status: 301},
193				{Filepath: "test/404.html", Status: 404},
194			},
195		},
196		{
197			Name: "redirect-full-url",
198			Actual: calcRoutes(
199				"test",
200				"/wow.html",
201				[]*RedirectRule{
202					{
203						From:   "/wow",
204						To:     "https://pico.sh",
205						Status: 301,
206					},
207				},
208			),
209			Expected: []*HttpReply{
210				{Filepath: "test/wow.html", Status: 200},
211				{Filepath: "/wow.html/", Status: 301},
212				{Filepath: "test/404.html", Status: 404},
213			},
214		},
215		{
216			Name: "redirect-root-full-url",
217			Actual: calcRoutes(
218				"test",
219				"/",
220				[]*RedirectRule{
221					{
222						From:   "/*",
223						To:     "https://pico.sh",
224						Status: 301,
225					},
226				},
227			),
228			Expected: []*HttpReply{
229				{Filepath: "test/index.html", Status: 200},
230				{Filepath: "https://pico.sh", Status: 301},
231			},
232		},
233		{
234			Name: "redirect-empty-route-full-url",
235			Actual: calcRoutes(
236				"test",
237				"",
238				[]*RedirectRule{
239					{
240						From:   "/*",
241						To:     "https://pico.sh",
242						Status: 301,
243					},
244				},
245			),
246			Expected: []*HttpReply{
247				{Filepath: "test/index.html", Status: 200},
248				{Filepath: "https://pico.sh", Status: 301},
249			},
250		},
251		{
252			Name: "redirect-full-url-directory",
253			Actual: calcRoutes(
254				"test",
255				"/wow",
256				[]*RedirectRule{
257					{
258						From:   "/wow",
259						To:     "https://pico.sh",
260						Status: 301,
261					},
262				},
263			),
264			Expected: []*HttpReply{
265				{Filepath: "test/wow", Status: 200},
266				{Filepath: "test/wow.html", Status: 200},
267				{Filepath: "https://pico.sh", Status: 301},
268			},
269		},
270		{
271			Name: "redirect-directory",
272			Actual: calcRoutes(
273				"public",
274				"/xyz",
275				[]*RedirectRule{
276					{
277						From:   "/xyz",
278						To:     "/wrk-xyz",
279						Status: 301,
280					},
281				},
282			),
283			Expected: []*HttpReply{
284				{Filepath: "public/xyz", Status: 200},
285				{Filepath: "public/xyz.html", Status: 200},
286				{Filepath: "/wrk-xyz", Status: 301},
287				{Filepath: "/xyz/", Status: 301},
288				{Filepath: "public/404.html", Status: 404},
289			},
290		},
291		{
292			Name: "redirect-sub-directory",
293			Actual: calcRoutes(
294				"public",
295				"/folder2",
296				[]*RedirectRule{
297					{
298						From:   "/folder2",
299						To:     "/folder",
300						Status: 200,
301					},
302				},
303			),
304			Expected: []*HttpReply{
305				{Filepath: "public/folder2", Status: 200},
306				{Filepath: "public/folder2.html", Status: 200},
307				{Filepath: "public/folder", Status: 200},
308				{Filepath: "public/folder.html", Status: 200},
309				{Filepath: "/folder/", Status: 301},
310				{Filepath: "public/404.html", Status: 404},
311			},
312		},
313		{
314			Name: "redirect-from-and-to-same",
315			Actual: calcRoutes(
316				"public",
317				"/folder2",
318				[]*RedirectRule{
319					{
320						From:   "/folder2",
321						To:     "/folder2",
322						Status: 200,
323					},
324				},
325			),
326			Expected: []*HttpReply{
327				{Filepath: "public/folder2", Status: 200},
328				{Filepath: "public/folder2.html", Status: 200},
329				{Filepath: "/folder2/", Status: 301},
330				{Filepath: "public/404.html", Status: 404},
331			},
332		},
333		{
334			Name: "redirect-no-trailing-slash",
335			Actual: calcRoutes(
336				"public",
337				"/space/",
338				[]*RedirectRule{
339					{
340						From:   "/space",
341						To:     "/frontier/",
342						Status: 301,
343					},
344				},
345			),
346			Expected: []*HttpReply{
347				{Filepath: "public/space/index.html", Status: 200},
348				{Filepath: "/frontier/", Status: 301},
349				{Filepath: "public/404.html", Status: 404},
350			},
351		},
352		{
353			Name: "redirect-with-trailing-slash",
354			Actual: calcRoutes(
355				"public",
356				"/space",
357				[]*RedirectRule{
358					{
359						From:   "/space/",
360						To:     "/frontier/",
361						Status: 301,
362					},
363				},
364			),
365			Expected: []*HttpReply{
366				{Filepath: "public/space", Status: 200},
367				{Filepath: "public/space.html", Status: 200},
368				{Filepath: "/frontier/", Status: 301},
369				{Filepath: "/space/", Status: 301},
370				{Filepath: "public/404.html", Status: 404},
371			},
372		},
373		{
374			Name: "directory-with-extension",
375			Actual: calcRoutes(
376				"public",
377				"/space.nvim",
378				[]*RedirectRule{},
379			),
380			Expected: []*HttpReply{
381				{Filepath: "public/space.nvim", Status: 200},
382				{Filepath: "public/space.nvim.html", Status: 200},
383				{Filepath: "/space.nvim/", Status: 301},
384				{Filepath: "public/404.html", Status: 404},
385			},
386		},
387		{
388			Name: "rewrite-to-site",
389			Actual: calcRoutes(
390				"public",
391				"/",
392				[]*RedirectRule{
393					{
394						From:   "/*",
395						To:     "https://my-other-site.pgs.sh/:splat",
396						Status: 200,
397					},
398				},
399			),
400			Expected: []*HttpReply{
401				{Filepath: "public/index.html", Status: 200},
402				{Filepath: "https://my-other-site.pgs.sh/", Status: 200},
403			},
404		},
405		{
406			Name: "rewrite-to-site-subdir",
407			Actual: calcRoutes(
408				"public",
409				"/plugin/nice/",
410				[]*RedirectRule{
411					{
412						From:   "/*",
413						To:     "https://my-other-site.pgs.sh/:splat",
414						Status: 200,
415					},
416				},
417			),
418			Expected: []*HttpReply{
419				{Filepath: "public/plugin/nice/index.html", Status: 200},
420				{Filepath: "https://my-other-site.pgs.sh/plugin/nice/", Status: 200},
421			},
422		},
423		{
424			Name: "rewrite-to-another-pgs-site",
425			Actual: calcRoutes(
426				"public",
427				"/my-site/index.html",
428				[]*RedirectRule{
429					{
430						From:   "/my-site/*",
431						To:     "https://my-other-site.pgs.sh/:splat",
432						Status: 200,
433					},
434				},
435			),
436			Expected: []*HttpReply{
437				{Filepath: "public/my-site/index.html", Status: 200},
438				{Filepath: "https://my-other-site.pgs.sh/index.html", Status: 200},
439			},
440		},
441		{
442			Name: "rewrite-placeholders",
443			Actual: calcRoutes(
444				"public",
445				"/news/02/12/2004/my-story",
446				[]*RedirectRule{
447					{
448						From:   "/news/:month/:date/:year/*",
449						To:     "/blog/:year/:month/:date/:splat",
450						Status: 200,
451					},
452				},
453			),
454			Expected: []*HttpReply{
455				{Filepath: "public/news/02/12/2004/my-story", Status: 200},
456				{Filepath: "public/news/02/12/2004/my-story.html", Status: 200},
457				{Filepath: "public/blog/2004/02/12/my-story", Status: 200},
458				{Filepath: "public/blog/2004/02/12/my-story.html", Status: 200},
459				{Filepath: "/blog/2004/02/12/my-story/", Status: 301},
460				{Filepath: "public/404.html", Status: 404},
461			},
462		},
463		{
464			Name: "302-redirect",
465			Actual: calcRoutes(
466				"public",
467				"/pages/chem351.html",
468				[]*RedirectRule{
469					{
470						From:   "/pages/chem351.html",
471						To:     "/pages/chem351",
472						Status: 302,
473						Force:  true,
474					},
475				},
476			),
477			Expected: []*HttpReply{
478				{Filepath: "/pages/chem351", Status: 302},
479				{Filepath: "/pages/chem351.html/", Status: 301},
480				{Filepath: "public/404.html", Status: 404},
481			},
482		},
483		{
484			Name: "302-redirect-non-match",
485			Actual: calcRoutes(
486				"public",
487				"/pages/chem351",
488				[]*RedirectRule{
489					{
490						From:   "/pages/chem351.html",
491						To:     "/pages/chem351",
492						Status: 302,
493						Force:  true,
494					},
495				},
496			),
497			Expected: []*HttpReply{
498				{Filepath: "public/pages/chem351", Status: 200},
499				{Filepath: "public/pages/chem351.html", Status: 200},
500				{Filepath: "/pages/chem351/", Status: 301},
501				{Filepath: "public/404.html", Status: 404},
502			},
503		},
504		{
505			Name: "wildcard-with-word",
506			Actual: calcRoutes(
507				"public",
508				"/pictures/soup",
509				[]*RedirectRule{
510					{
511						From:   "/pictures*",
512						To:     "https://super.fly.sh/:splat",
513						Status: 200,
514					},
515					{
516						From:   "/*",
517						To:     "https://super.fly.sh/:splat",
518						Status: 302,
519					},
520				},
521			),
522			Expected: []*HttpReply{
523				{Filepath: "public/pictures/soup", Status: 200},
524				{Filepath: "public/pictures/soup.html", Status: 200},
525				{Filepath: "https://super.fly.sh/soup", Status: 200},
526			},
527		},
528		{
529			Name: "wildcard-multiple",
530			Actual: calcRoutes(
531				"public",
532				"/super/ficial.html",
533				[]*RedirectRule{
534					{
535						From:   "/pictures*",
536						To:     "https://super.fly.sh/:splat",
537						Status: 200,
538					},
539					{
540						From:   "/*",
541						To:     "https://super.fly.sh/:splat",
542						Status: 302,
543					},
544				},
545			),
546			Expected: []*HttpReply{
547				{Filepath: "public/super/ficial.html", Status: 200},
548				{Filepath: "https://super.fly.sh/super/ficial.html", Status: 302},
549			},
550		},
551		{
552			Name: "well-known-splat-suffix",
553			Actual: calcRoutes(
554				"public",
555				"/.well-known/nodeinfo",
556				[]*RedirectRule{
557					{
558						From:   "/.well-known/nodeinfo*",
559						To:     "https://some.dev/.well-known/nodeinfo:splat",
560						Status: 301,
561					},
562				},
563			),
564			Expected: []*HttpReply{
565				{Filepath: "public/.well-known/nodeinfo", Status: 200},
566				{Filepath: "public/.well-known/nodeinfo.html", Status: 200},
567				{Filepath: "https://some.dev/.well-known/nodeinfo", Status: 301},
568			},
569		},
570		{
571			Name: "wildcard-query-param",
572			Actual: calcRoutes(
573				"public",
574				"/.well-known/webfinger?query=nice",
575				[]*RedirectRule{
576					{
577						From:   "/.well-known/webfinger*",
578						To:     "https://some.dev/.well-known/webfinger:splat",
579						Status: 301,
580					},
581				},
582			),
583			Expected: []*HttpReply{
584				{Filepath: "public/.well-known/webfinger?query=nice", Status: 200},
585				{Filepath: "public/.well-known/webfinger?query=nice.html", Status: 200},
586				{Filepath: "https://some.dev/.well-known/webfinger?query=nice", Status: 301},
587			},
588		},
589		{
590			Name: "well-known-splat-suffix-error",
591			Actual: calcRoutes(
592				"public",
593				"/software/",
594				[]*RedirectRule{
595					{
596						From:   "/.well-known/nodeinfo*",
597						To:     "https://some.dev/.well-known/nodeinfo:splat",
598						Status: 301,
599					},
600				},
601			),
602			Expected: []*HttpReply{
603				{Filepath: "public/software/index.html", Status: 200},
604				{Filepath: "public/404.html", Status: 404},
605			},
606		},
607		{
608			Name: "non-match-dont-redirect",
609			Actual: calcRoutes(
610				"public",
611				"/scripts/asd",
612				[]*RedirectRule{
613					{
614						From:   "/concat/*",
615						To:     "/scripts/:splat",
616						Status: 301,
617					},
618				},
619			),
620			Expected: []*HttpReply{
621				{Filepath: "public/scripts/asd", Status: 200},
622				{Filepath: "public/scripts/asd.html", Status: 200},
623				{Filepath: "/scripts/asd/", Status: 301},
624				{Filepath: "public/404.html", Status: 404},
625			},
626		},
627		{
628			Name: "implicit-prefix-slash-redirect",
629			Actual: calcRoutes(
630				"public",
631				"software/scripts",
632				[]*RedirectRule{
633					{
634						From:   "/software/concat/*",
635						To:     "/software/scripts/:splat",
636						Status: 301,
637					},
638				},
639			),
640			Expected: []*HttpReply{
641				{Filepath: "public/software/scripts", Status: 200},
642				{Filepath: "public/software/scripts.html", Status: 200},
643				{Filepath: "/software/scripts/", Status: 301},
644				{Filepath: "public/404.html", Status: 404},
645			},
646		},
647	}
648
649	for _, fixture := range fixtures {
650		t.Run(fixture.Name, func(t *testing.T) {
651			if cmp.Equal(fixture.Actual, fixture.Expected) == false {
652				//nolint
653				t.Fatal(cmp.Diff(fixture.Expected, fixture.Actual))
654			}
655		})
656	}
657}