Skip to content

Commit 49749f5

Browse files
committed
test: revert global property sets after each example
A property task's "Setting a global X" example has no global-clear counterpart, so the setting persisted for the rest of the run and broke later deploys - a leftover global network attachment or registry server made every subsequent app build fail against a resource the environment did not have. The driver now clears any global property an example sets once that example finishes, which generalizes the earlier network-specific workaround to every property family and removes the ordering sensitivity that surfaced different deploy failures per run.
1 parent f650844 commit 49749f5

1 file changed

Lines changed: 41 additions & 22 deletions

File tree

tasks/example_integration_test.go

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,6 @@ var exampleIntegrationPolicy = map[string]exampleReq{
102102
// be created first.
103103
"dokku_storage_mount": {ensureApps: []string{"node-js-app"}, setup: setupStorageMountExample},
104104

105-
// network_property references a network that must exist for a build to
106-
// attach to it, and its global example sets a property that would otherwise
107-
// make every later app build attach to that network.
108-
"dokku_network_property": {setup: setupNetworkPropertyExample},
109-
110105
// Tasks whose documented value is a placeholder the driver must provision
111106
// (a real inline PEM / cert path, a registry secret, or a maintenance
112107
// tarball) because it cannot be published in the docs.
@@ -195,6 +190,7 @@ func TestIntegrationTaskExamples(t *testing.T) {
195190
task = transform(task)
196191
}
197192
applyExample(t, example.Name, task)
193+
registerGlobalPropertyRevert(t, task)
198194
})
199195
}
200196
})
@@ -413,23 +409,6 @@ func enableHttpAuthExampleApp(t *testing.T) {
413409
}
414410
}
415411

416-
// setupNetworkPropertyExample creates the network the examples reference and,
417-
// on cleanup, clears the global attach-post-create property the global example
418-
// sets and removes the network. Without the cleanup, that global property
419-
// persists and makes every subsequent app build fail attaching to a network the
420-
// build environment no longer has.
421-
func setupNetworkPropertyExample(t *testing.T) (func(Task) Task, func()) {
422-
t.Helper()
423-
result := NetworkTask{Name: "example-network", State: StatePresent}.Execute()
424-
if result.Error != nil {
425-
t.Fatalf("failed to create example network: %v", result.Error)
426-
}
427-
return nil, func() {
428-
NetworkPropertyTask{Global: true, Property: "attach-post-create", State: StateAbsent}.Execute()
429-
NetworkTask{Name: "example-network", State: StateAbsent}.Execute()
430-
}
431-
}
432-
433412
// setupStorageMountExample creates the named storage entry the storage_mount
434413
// named-entry examples attach, and removes it afterward.
435414
func setupStorageMountExample(t *testing.T) (func(Task) Task, func()) {
@@ -516,6 +495,46 @@ func clearChalltestsrvA(host string) {
516495
})
517496
}
518497

498+
// registerGlobalPropertyRevert clears a global property an example just set, so
499+
// the setting does not persist and break later app builds or deploys. Property
500+
// tasks' "Setting a global X" examples have no global-clear counterpart (their
501+
// clear example targets the per-app scope), and a leftover global registry
502+
// server, network attachment, or similar makes every subsequent deploy fail.
503+
// It is a no-op for any task that is not a present/set of a global property.
504+
func registerGlobalPropertyRevert(t *testing.T, task Task) {
505+
t.Helper()
506+
v := reflect.Indirect(reflect.ValueOf(taskAsPointer(task)))
507+
if v.Kind() != reflect.Struct {
508+
return
509+
}
510+
global := v.FieldByName("Global")
511+
property := v.FieldByName("Property")
512+
state := v.FieldByName("State")
513+
if !global.IsValid() || global.Kind() != reflect.Bool || !global.Bool() {
514+
return
515+
}
516+
if !property.IsValid() || property.Kind() != reflect.String || property.String() == "" {
517+
return
518+
}
519+
if !state.IsValid() || state.Kind() != reflect.String {
520+
return
521+
}
522+
if s := State(state.String()); s != StatePresent && s != StateSet {
523+
return
524+
}
525+
clear := reflect.New(v.Type())
526+
clear.Elem().Set(v)
527+
clear.Elem().FieldByName("State").Set(reflect.ValueOf(StateAbsent))
528+
if value := clear.Elem().FieldByName("Value"); value.IsValid() && value.Kind() == reflect.String {
529+
value.SetString("")
530+
}
531+
clearTask, ok := clear.Interface().(Task)
532+
if !ok {
533+
return
534+
}
535+
t.Cleanup(func() { clearTask.Execute() })
536+
}
537+
519538
// taskStringField returns the value of a string field on a task struct, or ""
520539
// when the field is absent or not a string. It reads through a pointer so it
521540
// works whether the loader returned a value or pointer task.

0 commit comments

Comments
 (0)