package api_test import ( "net/http" "testing" ) // TestConfigSetVersioning exercises the set update/version/promote/fork/diff and // snapshot endpoints that the base end-to-end test does not reach. func TestConfigSetVersioning(t *testing.T) { srv, teardown := setup(t) defer teardown() mk := func(name string) string { resp := postJSON(t, srv, "/api/v1/config/sets", map[string]any{ "name": name, "parameters": []map[string]any{ {"key": "sp", "ds": "stub", "signal": "setpoint", "type": "float64", "default": 1.0}, }, }) assertStatus(t, resp, http.StatusCreated) var s struct { ID string `json:"id"` } readJSON(t, resp, &s) return s.ID } id := mk("Set A") // Update → version bump + backup. resp := putRaw(t, srv, "/api/v1/config/sets/"+id, "application/json", []byte(`{"name":"Set A v2","parameters":[{"key":"sp","ds":"stub","signal":"setpoint","type":"float64","default":2.0}]}`)) assertStatus(t, resp, http.StatusOK) // List versions. resp = get(t, srv, "/api/v1/config/sets/"+id+"/versions") assertStatus(t, resp, http.StatusOK) var versions []map[string]any readJSON(t, resp, &versions) if len(versions) < 2 { t.Fatalf("expected >=2 set versions, got %d", len(versions)) } // Get a specific version. resp = get(t, srv, "/api/v1/config/sets/"+id+"/versions/1") assertStatus(t, resp, http.StatusOK) resp.Body.Close() // Promote v1. resp = postRaw(t, srv, "/api/v1/config/sets/"+id+"/versions/1/promote", "application/json", nil) assertStatus(t, resp, http.StatusOK) // Fork v1 → new set id. resp = postRaw(t, srv, "/api/v1/config/sets/"+id+"/versions/1/fork", "application/json", nil) assertStatus(t, resp, http.StatusCreated) var fork struct { ID string `json:"id"` } readJSON(t, resp, &fork) if fork.ID == "" || fork.ID == id { t.Errorf("fork id = %q, want fresh id", fork.ID) } // Diff the original against the fork. resp = get(t, srv, "/api/v1/config/sets/diff?a="+id+"&b="+fork.ID) assertStatus(t, resp, http.StatusOK) resp.Body.Close() // Diff with a missing left id → 400. resp = get(t, srv, "/api/v1/config/sets/diff?a=&b="+fork.ID) assertStatus(t, resp, http.StatusBadRequest) // Snapshot the set into a new instance. resp = postJSON(t, srv, "/api/v1/config/sets/"+id+"/snapshot", map[string]any{"name": "snap-1"}) assertStatus(t, resp, http.StatusCreated) resp.Body.Close() } // TestConfigInstanceVersioning exercises the instance update/version/promote/ // fork/validate and live-diff endpoints. func TestConfigInstanceVersioning(t *testing.T) { srv, teardown := setup(t) defer teardown() // A set to bind instances to. resp := postJSON(t, srv, "/api/v1/config/sets", map[string]any{ "name": "Set B", "parameters": []map[string]any{ {"key": "sp", "ds": "stub", "signal": "setpoint", "type": "float64", "default": 1.0}, }, }) assertStatus(t, resp, http.StatusCreated) var set struct { ID string `json:"id"` } readJSON(t, resp, &set) // Create an instance. resp = postJSON(t, srv, "/api/v1/config/instances", map[string]any{ "name": "inst", "setId": set.ID, "values": map[string]any{"sp": 5.0}, }) assertStatus(t, resp, http.StatusCreated) var inst struct { ID string `json:"id"` } readJSON(t, resp, &inst) // Get it. resp = get(t, srv, "/api/v1/config/instances/"+inst.ID) assertStatus(t, resp, http.StatusOK) resp.Body.Close() // List instances. resp = get(t, srv, "/api/v1/config/instances") assertStatus(t, resp, http.StatusOK) resp.Body.Close() // Update → version bump. resp = putRaw(t, srv, "/api/v1/config/instances/"+inst.ID, "application/json", []byte(`{"name":"inst v2","setId":"`+set.ID+`","values":{"sp":7.0}}`)) assertStatus(t, resp, http.StatusOK) // List versions. resp = get(t, srv, "/api/v1/config/instances/"+inst.ID+"/versions") assertStatus(t, resp, http.StatusOK) var versions []map[string]any readJSON(t, resp, &versions) if len(versions) < 2 { t.Fatalf("expected >=2 instance versions, got %d", len(versions)) } // Get a version. resp = get(t, srv, "/api/v1/config/instances/"+inst.ID+"/versions/1") assertStatus(t, resp, http.StatusOK) resp.Body.Close() // Promote + fork. resp = postRaw(t, srv, "/api/v1/config/instances/"+inst.ID+"/versions/1/promote", "application/json", nil) assertStatus(t, resp, http.StatusOK) resp = postRaw(t, srv, "/api/v1/config/instances/"+inst.ID+"/versions/1/fork", "application/json", nil) assertStatus(t, resp, http.StatusCreated) resp.Body.Close() // Validate against the (empty) rule set → 200. resp = postJSON(t, srv, "/api/v1/config/instances/"+inst.ID+"/validate", nil) assertStatus(t, resp, http.StatusOK) resp.Body.Close() // Live diff against current signal values → 200. resp = get(t, srv, "/api/v1/config/instances/"+inst.ID+"/livediff") assertStatus(t, resp, http.StatusOK) resp.Body.Close() // Delete the instance. resp = deleteReq(t, srv, "/api/v1/config/instances/"+inst.ID) assertStatus(t, resp, http.StatusNoContent) }