Files
uopi/internal/api/confmgr_rules_test.go
T
Martino Ferrari c0f7e662be Testing
2026-06-24 01:39:15 +02:00

147 lines
4.3 KiB
Go

package api_test
import (
"net/http"
"testing"
)
// TestConfigRuleCRUD exercises the full config-rule REST surface: list, create,
// get, update, versioning (list/get/promote/fork), check, preview, and delete,
// plus the principal error paths.
func TestConfigRuleCRUD(t *testing.T) {
srv, teardown := setup(t)
defer teardown()
// A set the rule (and preview) can bind to.
resp := postJSON(t, srv, "/api/v1/config/sets", map[string]any{
"name": "S",
"parameters": []map[string]any{
{"key": "voltage", "ds": "stub", "signal": "setpoint", "type": "float64", "default": 12.0},
},
})
assertStatus(t, resp, http.StatusCreated)
var set struct {
ID string `json:"id"`
}
readJSON(t, resp, &set)
// Empty list initially.
resp = get(t, srv, "/api/v1/config/rules")
assertStatus(t, resp, http.StatusOK)
var list []map[string]any
readJSON(t, resp, &list)
if len(list) != 0 {
t.Fatalf("expected no rules, got %d", len(list))
}
// Create.
resp = postJSON(t, srv, "/api/v1/config/rules", map[string]any{
"name": "cap",
"setId": set.ID,
"source": "voltage: <=24",
})
assertStatus(t, resp, http.StatusCreated)
var rule struct {
ID string `json:"id"`
Version int `json:"version"`
}
readJSON(t, resp, &rule)
if rule.ID == "" {
t.Fatal("rule missing id")
}
// Create with invalid CUE → 400.
resp = postJSON(t, srv, "/api/v1/config/rules", map[string]any{
"name": "bad",
"setId": set.ID,
"source": "voltage: <=",
})
assertStatus(t, resp, http.StatusBadRequest)
// Create with malformed JSON → 400.
resp = postRaw(t, srv, "/api/v1/config/rules", "application/json", []byte(`{not json`))
assertStatus(t, resp, http.StatusBadRequest)
// Get.
resp = get(t, srv, "/api/v1/config/rules/"+rule.ID)
assertStatus(t, resp, http.StatusOK)
resp.Body.Close()
// Get missing → 404.
resp = get(t, srv, "/api/v1/config/rules/nope")
assertStatus(t, resp, http.StatusNotFound)
// Update (bumps version, creates backup).
resp = putRaw(t, srv, "/api/v1/config/rules/"+rule.ID, "application/json",
[]byte(`{"name":"cap2","setId":"`+set.ID+`","source":"voltage: <=30"}`))
assertStatus(t, resp, http.StatusOK)
// List versions.
resp = get(t, srv, "/api/v1/config/rules/"+rule.ID+"/versions")
assertStatus(t, resp, http.StatusOK)
var versions []map[string]any
readJSON(t, resp, &versions)
if len(versions) < 2 {
t.Fatalf("expected >=2 rule versions, got %d", len(versions))
}
// Get v1.
resp = get(t, srv, "/api/v1/config/rules/"+rule.ID+"/versions/1")
assertStatus(t, resp, http.StatusOK)
resp.Body.Close()
// Bad version path → 400.
resp = get(t, srv, "/api/v1/config/rules/"+rule.ID+"/versions/xyz")
assertStatus(t, resp, http.StatusBadRequest)
// Promote v1.
resp = postRaw(t, srv, "/api/v1/config/rules/"+rule.ID+"/versions/1/promote", "application/json", nil)
assertStatus(t, resp, http.StatusOK)
// Fork v1 → new id.
resp = postRaw(t, srv, "/api/v1/config/rules/"+rule.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 == rule.ID {
t.Errorf("fork id = %q, want fresh id", fork.ID)
}
// Check (live validation of an unsaved source against sample values).
resp = postJSON(t, srv, "/api/v1/config/rules/check", map[string]any{
"source": "voltage: <=24",
"values": map[string]any{"voltage": 20.0},
})
assertStatus(t, resp, http.StatusOK)
resp.Body.Close()
// Check with malformed JSON → 400.
resp = postRaw(t, srv, "/api/v1/config/rules/check", "application/json", []byte(`{`))
assertStatus(t, resp, http.StatusBadRequest)
// Preview against the bound set's live signals.
resp = postJSON(t, srv, "/api/v1/config/rules/preview", map[string]any{
"setId": set.ID,
"source": "voltage: <=24",
})
assertStatus(t, resp, http.StatusOK)
resp.Body.Close()
// Preview with an unknown set → 404.
resp = postJSON(t, srv, "/api/v1/config/rules/preview", map[string]any{
"setId": "does-not-exist",
"source": "voltage: <=24",
})
assertStatus(t, resp, http.StatusNotFound)
// Delete.
resp = deleteReq(t, srv, "/api/v1/config/rules/"+rule.ID)
assertStatus(t, resp, http.StatusNoContent)
// Delete missing → 404.
resp = deleteReq(t, srv, "/api/v1/config/rules/"+rule.ID)
assertStatus(t, resp, http.StatusNotFound)
}