Skip to content

test(config): pin resolved config with golden tests#73

Merged
obcode merged 1 commit into
mainfrom
test/config-golden-net
Jul 17, 2026
Merged

test(config): pin resolved config with golden tests#73
obcode merged 1 commit into
mainfrom
test/config-golden-net

Conversation

@obcode

@obcode obcode commented Jul 17, 2026

Copy link
Copy Markdown
Owner

Schritt 0 des glabs-web-Plans: das Sicherheitsnetz, bevor irgendetwas am Config-Laden angefasst wird.

Warum

config/ baut AssignmentConfig heute aus ~63 einzelnen viper.GetString("kurs.assignment.feld")-Aufrufen zusammen — ohne yaml-Tags, mit Vererbung, die beim Lesen in globalen State zurückschreibt. Der geplante Umbau auf ein typisiertes Quellschema muss verhaltenserhaltend sein, und das lässt sich nur beweisen, wenn das IST-Verhalten vorher festgenagelt ist.

Dieser PR ändert keinen Produktivcode — nur Tests und Fixtures.

Was drin ist

Fixtures (config/testdata/courses/):

  • mpd, vss, algdati, fundc, fun — strukturgetreue Kopien der echten Kursdateien, Studi-Identitäten pseudonymisiert. Keine echten Adressen im öffentlichen Repo; verifiziert per Diff, dass sonst nichts an der Struktur abweicht.
  • casecourse — synthetisch, und der eigentliche Grund für diesen PR. Die echten YAMLs sind durchgängig kleingeschrieben und können eine Änderung am Case-Handling nicht entdecken. Dieses Fixture nagelt die Asymmetrie fest, an der ein naiver yaml.v3-Decoder zerbräche:
    • viper schreibt Map-Keys klein (Grp01grp01), lässt Listen-Werte aber in Ruhe
    • gitlabProjectPath behält bereits gültige Pfade verbatim (Upper.Case), parameterisiert ungültige (plus+tagplus-tag)
  • legacy — synthetisch. Nagelt die Legacy-Key-Shims und ihre Aktivierungsregeln fest (die startercode-Branch-Shims greifen nur, wenn branches: fehlt; die Issue-Shims nur, wenn issues: ganz fehlt). Die meisten davon haben in den echten Dateien null Abdeckung.

Der Golden-View zeichnet die abgeleiteten Repo-Namen auf, nicht nur die Rohconfig. Das ist der Punkt: eine Änderung am Case-Handling taucht in Email/Raw gar nicht auf — sie benennt still das GitLab-Projekt um. Gegenprobe gemacht: eine künstlich eingebaute Regression wird gefangen und zeigt exakt den geänderten repoName.

viper wird pro Assignment neu geladen, weil GetAssignmentConfig das gemergte extends-Ergebnis in den globalen State zurückschreibt — sonst hinge das Ergebnis von der Auflösungsreihenfolge ab.

Zwei Bugs gefunden — bewusst eingefroren, nicht gefixt

Die Goldens frieren das Verhalten ein, inklusive Bugs. Sonst weiß man beim späteren Umbau bei jedem Diff nicht, ob er gewollt ist. Beide sind in den Fixtures dokumentiert und gehören in eigene Commits nach dem Schema-Umbau:

  1. release.dockerImages wird vom falschen Key gelesen. vss.yaml legt die Images unter release.mergeRequest.dockerImages, config/release.go:47 liest release.dockerImages → die sechs Images in vss/blatt2 werden stillschweigend ignoriert.
  2. Der approvalsRequired-Alias ist toter Code. viper schreibt den Key auf approvalsrequired klein, bevor die Alias-Tabelle in config/assignment.go:443 ihn gegen die CamelCase-Schreibweise vergleicht — der Fall trifft nie zu. Ergebnis: RequiredApprovals: 0 statt des konfigurierten Werts, also keine Approval-Pflicht. Der Golden legacy.approvalslist.json stellt die drei Schreibweisen nebeneinander: required_approvals→2 ✓, approvalsRequired→0 ✗, requiredApprovals→3 ✓.

Beide sind latent, nicht aktiv: keine echte Config nutzt den kaputten Alias, und vss/blatt2 setzt containerRegistry ohnehin explizit. Genau die Klasse Fund, die das geplante glabs config lint künftig automatisch meldet.

Verifikation

  • go test ./... grün; gofmt, go vet, golangci-lint sauber
  • Determinismus: zweimal -update erzeugt keine Diffs
  • Netz greift: künstliche Regression wird gefangen
  • Kein Leck: 0 Treffer für @hm.edu oder lokale Pfade im Diff, 3589 Pseudonyme

Refresh: go test ./config/ -run TestGolden -update

🤖 Generated with Claude Code

Add a golden-test net over GetAssignmentConfig so the upcoming rewrite of
config loading (viper -> typed source schema) can be proven behaviour-
preserving rather than merely compiling.

Fixtures in config/testdata/courses/:
  - mpd, vss, algdati, fundc, fun: structural copies of the real course
    files, student identities pseudonymized (no real addresses in the repo)
  - casecourse: synthetic. The real files are lowercase throughout and
    therefore cannot detect a change in case handling. This one pins the
    key/value asymmetry that a naive yaml.v3 decoder would break: viper
    lowercases map keys (Grp01 -> grp01) but leaves list values alone, and
    gitlabProjectPath keeps already-valid paths verbatim (Upper.Case) while
    parameterizing invalid ones (plus+tag -> plus-tag).
  - legacy: synthetic. Pins the legacy-key shims and their activation rules,
    most of which have no coverage in the real files.

The view records the *derived* repo names, not just the raw config: a change
in case handling would not show up in Email/Raw, but it silently renames the
GitLab project. Verified by injecting a regression: the goldens catch it.

viper is reloaded per assignment because GetAssignmentConfig writes the
merged `extends` result back into global state, so results would otherwise
depend on resolution order.

Behaviour is frozen as-is, bugs included. Two are documented in the fixtures
and must be fixed separately, after the rewrite, so migration diffs stay
readable:
  - release.dockerImages is read from the wrong key, silently ignoring the
    images configured in vss/blatt2
  - the `approvalsRequired` alias is dead code (viper lowercases it before
    the alias table sees it), silently yielding RequiredApprovals 0 instead
    of the configured value

Refresh with: go test ./config/ -run TestGolden -update

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Coverage

github.com/obcode/glabs/v2/cmd/addgroupguests.go:12:	init						100.0%
github.com/obcode/glabs/v2/cmd/archive.go:12:		init						100.0%
github.com/obcode/glabs/v2/cmd/check.go:10:		init						100.0%
github.com/obcode/glabs/v2/cmd/clone.go:45:		init						100.0%
github.com/obcode/glabs/v2/cmd/delete.go:12:		init						100.0%
github.com/obcode/glabs/v2/cmd/generate.go:12:		init						100.0%
github.com/obcode/glabs/v2/cmd/protect.go:12:		init						100.0%
github.com/obcode/glabs/v2/cmd/push.go:35:		init						100.0%
github.com/obcode/glabs/v2/cmd/report.go:14:		init						100.0%
github.com/obcode/glabs/v2/cmd/root.go:39:		Execute						0.0%
github.com/obcode/glabs/v2/cmd/root.go:43:		init						100.0%
github.com/obcode/glabs/v2/cmd/root.go:51:		er						0.0%
github.com/obcode/glabs/v2/cmd/root.go:56:		initConfig					0.0%
github.com/obcode/glabs/v2/cmd/setaccess.go:12:		init						100.0%
github.com/obcode/glabs/v2/cmd/show.go:8:		init						100.0%
github.com/obcode/glabs/v2/cmd/update.go:12:		init						100.0%
github.com/obcode/glabs/v2/cmd/urls.go:31:		init						100.0%
github.com/obcode/glabs/v2/cmd/version.go:10:		init						100.0%
github.com/obcode/glabs/v2/config/assignment.go:12:	GetCourseURL					0.0%
github.com/obcode/glabs/v2/config/assignment.go:16:	GetAssignmentConfig				93.0%
github.com/obcode/glabs/v2/config/assignment.go:129:	RepoSuffix					100.0%
github.com/obcode/glabs/v2/config/assignment.go:148:	RepoBaseName					100.0%
github.com/obcode/glabs/v2/config/assignment.go:163:	RepoNameWithSuffix				100.0%
github.com/obcode/glabs/v2/config/assignment.go:167:	RepoNameForStudent				100.0%
github.com/obcode/glabs/v2/config/assignment.go:171:	RepoNameForGroup				100.0%
github.com/obcode/glabs/v2/config/assignment.go:175:	coursePath					100.0%
github.com/obcode/glabs/v2/config/assignment.go:186:	GetCourseSubgroupPath				0.0%
github.com/obcode/glabs/v2/config/assignment.go:190:	assignmentPath					100.0%
github.com/obcode/glabs/v2/config/assignment.go:201:	per						100.0%
github.com/obcode/glabs/v2/config/assignment.go:208:	description					100.0%
github.com/obcode/glabs/v2/config/assignment.go:218:	mergeRequest					91.7%
github.com/obcode/glabs/v2/config/assignment.go:253:	mergeRequestApprovals				93.9%
github.com/obcode/glabs/v2/config/assignment.go:333:	extractMergeRequestApprovalRulesRaw		80.0%
github.com/obcode/glabs/v2/config/assignment.go:346:	containsLegacyApprovalUsersKey			56.2%
github.com/obcode/glabs/v2/config/assignment.go:377:	mergeRequestApprovalSettings			91.7%
github.com/obcode/glabs/v2/config/assignment.go:425:	normalizeMergeRequestApprovalConfigKeys		100.0%
github.com/obcode/glabs/v2/config/course.go:16:		GetCourseConfig					50.0%
github.com/obcode/glabs/v2/config/course.go:32:		CourseExists					0.0%
github.com/obcode/glabs/v2/config/course.go:37:		StudentKey					0.0%
github.com/obcode/glabs/v2/config/inheritance.go:28:	assignmentIsAbstract				100.0%
github.com/obcode/glabs/v2/config/inheritance.go:44:	resolveAssignmentInheritance			100.0%
github.com/obcode/glabs/v2/config/inheritance.go:59:	mergedAssignmentMap				80.0%
github.com/obcode/glabs/v2/config/inheritance.go:100:	deepMerge					100.0%
github.com/obcode/glabs/v2/config/inheritance.go:121:	asStringMap					42.9%
github.com/obcode/glabs/v2/config/release.go:8:		release						100.0%
github.com/obcode/glabs/v2/config/release.go:22:	releaseMergeRequest				100.0%
github.com/obcode/glabs/v2/config/release.go:46:	dockerImages					100.0%
github.com/obcode/glabs/v2/config/repo.go:11:		startercode					91.3%
github.com/obcode/glabs/v2/config/repo.go:60:		branches					91.5%
github.com/obcode/glabs/v2/config/repo.go:141:		normalizeBranchRuleConfigKeys			100.0%
github.com/obcode/glabs/v2/config/repo.go:172:		defaultBranch					62.5%
github.com/obcode/glabs/v2/config/repo.go:187:		issues						100.0%
github.com/obcode/glabs/v2/config/repo.go:209:		clone						100.0%
github.com/obcode/glabs/v2/config/repo.go:231:		SetBranch					100.0%
github.com/obcode/glabs/v2/config/repo.go:235:		SetProtectToBranch				88.9%
github.com/obcode/glabs/v2/config/repo.go:253:		SetLocalpath					100.0%
github.com/obcode/glabs/v2/config/repo.go:257:		SetForce					100.0%
github.com/obcode/glabs/v2/config/seeder.go:15:		seeder						53.8%
github.com/obcode/glabs/v2/config/show.go:10:		Show						93.1%
github.com/obcode/glabs/v2/config/slug.go:44:		gitlabProjectPath				100.0%
github.com/obcode/glabs/v2/config/slug.go:60:		gitlabGroupPath					100.0%
github.com/obcode/glabs/v2/config/slug.go:75:		gitlabGroupPathSegment				100.0%
github.com/obcode/glabs/v2/config/slug.go:93:		parameterize					100.0%
github.com/obcode/glabs/v2/config/students.go:14:	SetAccessLevel					100.0%
github.com/obcode/glabs/v2/config/students.go:28:	accessLevel					100.0%
github.com/obcode/glabs/v2/config/students.go:43:	students					100.0%
github.com/obcode/glabs/v2/config/students.go:75:	mkStudents					94.4%
github.com/obcode/glabs/v2/config/students.go:108:	groups						100.0%
github.com/obcode/glabs/v2/config/types.go:5:		String						100.0%
github.com/obcode/glabs/v2/config/urls.go:9:		StartercodeURL					60.0%
github.com/obcode/glabs/v2/config/urls.go:18:		gitURLToWebURL					56.0%
github.com/obcode/glabs/v2/config/urls.go:61:		Urls						100.0%
github.com/obcode/glabs/v2/git/auth.go:11:		GetAuth						90.0%
github.com/obcode/glabs/v2/git/clone.go:18:		Clone						80.0%
github.com/obcode/glabs/v2/git/clone.go:38:		ProjectRepoUrl					100.0%
github.com/obcode/glabs/v2/git/clone.go:44:		localpath					100.0%
github.com/obcode/glabs/v2/git/clone.go:48:		clone						36.1%
github.com/obcode/glabs/v2/git/push.go:17:		Push						0.0%
github.com/obcode/glabs/v2/git/sourcerepo.go:19:	PrepareSourceRepo				0.0%
github.com/obcode/glabs/v2/gitlab/approvals.go:15:	applyMergeRequestApprovalRules			100.0%
github.com/obcode/glabs/v2/gitlab/approvals.go:19:	applyMergeRequestApprovalRulesForMemberCount	80.7%
github.com/obcode/glabs/v2/gitlab/approvals.go:222:	applyMergeRequestApprovalSettings		73.8%
github.com/obcode/glabs/v2/gitlab/approvals.go:285:	approvalRuleName				60.0%
github.com/obcode/glabs/v2/gitlab/approvals.go:295:	approvalRuleAppliesForMemberCount		100.0%
github.com/obcode/glabs/v2/gitlab/approvals.go:305:	resolveApprovalUsernames			77.8%
github.com/obcode/glabs/v2/gitlab/approvals.go:337:	resolveApprovalGroupIDs				75.0%
github.com/obcode/glabs/v2/gitlab/approvals.go:364:	isNumericIdentifier				75.0%
github.com/obcode/glabs/v2/gitlab/archive.go:14:	Archive						77.8%
github.com/obcode/glabs/v2/gitlab/archive.go:32:	archivePerStudent				90.9%
github.com/obcode/glabs/v2/gitlab/archive.go:54:	archivePerGroup					90.9%
github.com/obcode/glabs/v2/gitlab/archive.go:76:	archive						75.6%
github.com/obcode/glabs/v2/gitlab/branches.go:11:	syncConfiguredBranches				50.0%
github.com/obcode/glabs/v2/gitlab/branches.go:47:	defaultBranchName				37.5%
github.com/obcode/glabs/v2/gitlab/branches.go:65:	isBranchAlreadyExistsError			0.0%
github.com/obcode/glabs/v2/gitlab/check.go:9:		CheckCourse					90.6%
github.com/obcode/glabs/v2/gitlab/check.go:67:		checkStudent					100.0%
github.com/obcode/glabs/v2/gitlab/check.go:89:		checkDupsInGroups				100.0%
github.com/obcode/glabs/v2/gitlab/delete.go:11:		Delete						77.8%
github.com/obcode/glabs/v2/gitlab/delete.go:29:		deletePerStudent				100.0%
github.com/obcode/glabs/v2/gitlab/delete.go:40:		deletePerGroup					100.0%
github.com/obcode/glabs/v2/gitlab/delete.go:51:		delete						92.9%
github.com/obcode/glabs/v2/gitlab/generate.go:14:	Generate					38.9%
github.com/obcode/glabs/v2/gitlab/generate.go:56:	generate					0.0%
github.com/obcode/glabs/v2/gitlab/generate.go:273:	generatePerStudent				0.0%
github.com/obcode/glabs/v2/gitlab/generate.go:286:	generatePerGroup				0.0%
github.com/obcode/glabs/v2/gitlab/gitlab.go:13:		NewClient					80.0%
github.com/obcode/glabs/v2/gitlab/groups.go:13:		getGroupIDByFullPath				100.0%
github.com/obcode/glabs/v2/gitlab/groups.go:34:		getGroupID					100.0%
github.com/obcode/glabs/v2/gitlab/groups.go:47:		createGroup					83.3%
github.com/obcode/glabs/v2/gitlab/groups.go:88:		AddGroupGuests					0.0%
github.com/obcode/glabs/v2/gitlab/groups.go:142:	inviteGroupByEmail				0.0%
github.com/obcode/glabs/v2/gitlab/groups.go:160:	addGroupMember					0.0%
github.com/obcode/glabs/v2/gitlab/groups.go:197:	collectUniqueStudents				0.0%
github.com/obcode/glabs/v2/gitlab/issues.go:99:		getStartercodeProject				100.0%
github.com/obcode/glabs/v2/gitlab/issues.go:143:	replicateIssue					37.0%
github.com/obcode/glabs/v2/gitlab/issues.go:202:	resolveIssuePlanForReplication			83.3%
github.com/obcode/glabs/v2/gitlab/issues.go:245:	resolveIssueNumbersForReplication		75.0%
github.com/obcode/glabs/v2/gitlab/issues.go:254:	loadIssueForReplication				69.6%
github.com/obcode/glabs/v2/gitlab/issues.go:298:	listChildIIDsByParentLookup			46.2%
github.com/obcode/glabs/v2/gitlab/issues.go:346:	listChildIIDsByIssueGraphQL			82.4%
github.com/obcode/glabs/v2/gitlab/issues.go:381:	workItemTypeIDForName				0.0%
github.com/obcode/glabs/v2/gitlab/issues.go:406:	attachChildTaskToParent				0.0%
github.com/obcode/glabs/v2/gitlab/issues.go:429:	getProjectPathForGraphQL			25.0%
github.com/obcode/glabs/v2/gitlab/projects.go:12:	generateProject					77.5%
github.com/obcode/glabs/v2/gitlab/projects.go:102:	getProjectByName				80.0%
github.com/obcode/glabs/v2/gitlab/protect.go:15:	ProtectToBranch					77.8%
github.com/obcode/glabs/v2/gitlab/protect.go:33:	protectBranch					100.0%
github.com/obcode/glabs/v2/gitlab/protect.go:37:	protectBranchForMemberCount			71.8%
github.com/obcode/glabs/v2/gitlab/protect.go:119:	hasProtectedBranches				100.0%
github.com/obcode/glabs/v2/gitlab/protect.go:129:	hasMergeRequestApprovalConfig			100.0%
github.com/obcode/glabs/v2/gitlab/protect.go:137:	protectSingleBranch				72.2%
github.com/obcode/glabs/v2/gitlab/protect.go:191:	recreateProtectedBranch				80.0%
github.com/obcode/glabs/v2/gitlab/protect.go:229:	replaceBranchPermissions			88.9%
github.com/obcode/glabs/v2/gitlab/protect.go:245:	isProtectedBranchNotFoundError			75.0%
github.com/obcode/glabs/v2/gitlab/protect.go:254:	protectToBranchPerStudent			90.9%
github.com/obcode/glabs/v2/gitlab/protect.go:276:	protectToBranchPerGroup				72.7%
github.com/obcode/glabs/v2/gitlab/push.go:10:		Push						0.0%
github.com/obcode/glabs/v2/gitlab/report.go:14:		Report						78.9%
github.com/obcode/glabs/v2/gitlab/report.go:51:		ReportHTML					73.7%
github.com/obcode/glabs/v2/gitlab/report.go:86:		ReportJSON					77.8%
github.com/obcode/glabs/v2/gitlab/report_helper.go:17:	report						82.5%
github.com/obcode/glabs/v2/gitlab/report_helper.go:135:	projectReport					85.9%
github.com/obcode/glabs/v2/gitlab/seeder.go:21:		localpath					0.0%
github.com/obcode/glabs/v2/gitlab/seeder.go:25:		runSeeder					0.0%
github.com/obcode/glabs/v2/gitlab/seeder.go:100:	addAndCommit					0.0%
github.com/obcode/glabs/v2/gitlab/seeder.go:121:	push						0.0%
github.com/obcode/glabs/v2/gitlab/setaccess.go:14:	Setaccess					77.8%
github.com/obcode/glabs/v2/gitlab/setaccess.go:32:	setaccess					41.5%
github.com/obcode/glabs/v2/gitlab/setaccess.go:140:	inviteByEmail					100.0%
github.com/obcode/glabs/v2/gitlab/setaccess.go:155:	setaccessPerStudent				100.0%
github.com/obcode/glabs/v2/gitlab/setaccess.go:175:	setaccessPerGroup				80.0%
github.com/obcode/glabs/v2/gitlab/starterrepo.go:16:	pushStartercode					0.0%
github.com/obcode/glabs/v2/gitlab/update.go:15:		Update						60.0%
github.com/obcode/glabs/v2/gitlab/update.go:49:		update						31.6%
github.com/obcode/glabs/v2/gitlab/update.go:96:		updatePerStudent				100.0%
github.com/obcode/glabs/v2/gitlab/update.go:116:	updatePerGroup					100.0%
github.com/obcode/glabs/v2/gitlab/users.go:12:		getUser						95.0%
github.com/obcode/glabs/v2/gitlab/users.go:49:		getUserID					100.0%
github.com/obcode/glabs/v2/gitlab/users.go:63:		addMember					94.1%
github.com/obcode/glabs/v2/main.go:19:			main						0.0%
total:							(statements)					64.3%

@obcode
obcode merged commit 6c62e30 into main Jul 17, 2026
10 checks passed
@obcode
obcode deleted the test/config-golden-net branch July 17, 2026 12:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant