aboutsummaryrefslogtreecommitdiff
path: root/internal/game/snapshot_render_guard_test.go
blob: 83833d5768729515485d9a3d28a07034f83161c1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package game

// Guard for the last per-game presentation seam: the Snapshot marker interface
// guarantees an event carries a structured view, but renderCasinoSnapshot's
// type switch silently returns "" for a snapshot it does not know. This
// source-level test pairs every `func (*X) snapshot() {}` in the casino package
// with a `case *casino.X:` in the renderer, so adding a game cannot silently
// drop its view (the text message would still appear, hiding the loss).

import (
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"testing"
)

func TestEveryCasinoSnapshotHasARenderer(t *testing.T) {
	typesSrc, err := os.ReadFile(filepath.Join("..", "casino", "types.go"))
	if err != nil {
		t.Fatalf("read casino types: %v", err)
	}
	renderSrc, err := os.ReadFile("casino.go")
	if err != nil {
		t.Fatalf("read renderer: %v", err)
	}

	re := regexp.MustCompile(`func \(\*(\w+)\) snapshot\(\)`)
	matches := re.FindAllStringSubmatch(string(typesSrc), -1)
	if len(matches) == 0 {
		t.Fatal("no Snapshot implementations found in casino/types.go")
	}
	for _, m := range matches {
		name := m[1]
		want := "case *casino." + name + ":"
		if !strings.Contains(string(renderSrc), want) {
			t.Errorf("snapshot %s has no renderer: add `%s` to renderCasinoSnapshot", name, want)
		}
	}
}