mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-01 04:50:16 +00:00
This commit contains multiple fixes for temperature proxy registration, but the core issue remains unresolved. ## What's Fixed: 1. Added config pointer and reloadFunc to TemperatureProxyHandlers 2. Added SetConfig method to keep handler in sync with router config changes 3. Added config reload after registration to prevent monitor from overwriting 4. Fixed installer port conflict detection and duplicate YAML key issues 5. Added comprehensive debug logging throughout registration flow ## What's Still Broken: The TemperatureProxyURL, TemperatureProxyToken, and TemperatureProxyControlToken fields are NOT persisting to nodes.enc after SaveNodesConfig is called. Debug logs confirm: - HandleRegister correctly updates nodesConfig.PVEInstances[matchedIndex] - The correct data is passed to SaveNodesConfig (verified in logs) - SaveNodesConfig completes without errors - Config reload executes successfully - BUT after Pulse restart, the fields are empty when loaded from disk The bug is in SaveNodesConfig serialization or file writing logic itself. Related files: - internal/api/temperature_proxy.go: Registration handler - internal/config/persistence.go: SaveNodesConfig implementation - internal/config/config.go: PVEInstance struct definition
144 lines
4.1 KiB
Go
144 lines
4.1 KiB
Go
package api
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/config"
|
|
)
|
|
|
|
func TestHandleSetupScriptRejectsUnsafeAuthToken(t *testing.T) {
|
|
tempDir := t.TempDir()
|
|
cfg := &config.Config{
|
|
DataPath: tempDir,
|
|
ConfigPath: tempDir,
|
|
}
|
|
|
|
handlers := newTestConfigHandlers(t, cfg)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/setup-script?type=pve&host=https://example.com&auth_token=$(touch%20/tmp/pwned)", nil)
|
|
rr := httptest.NewRecorder()
|
|
|
|
handlers.HandleSetupScript(rr, req)
|
|
|
|
if rr.Code != http.StatusBadRequest {
|
|
t.Fatalf("expected 400 bad request for unsafe auth token, got %d (%s)", rr.Code, rr.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestHandleSetupScriptRejectsUnsafePulseURL(t *testing.T) {
|
|
tempDir := t.TempDir()
|
|
cfg := &config.Config{
|
|
DataPath: tempDir,
|
|
ConfigPath: tempDir,
|
|
}
|
|
|
|
handlers := newTestConfigHandlers(t, cfg)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/setup-script?type=pve&host=https://example.com&pulse_url=http://example.com%5C%0Aecho%20oops", nil)
|
|
rr := httptest.NewRecorder()
|
|
|
|
handlers.HandleSetupScript(rr, req)
|
|
|
|
if rr.Code != http.StatusBadRequest {
|
|
t.Fatalf("expected 400 bad request for unsafe pulse_url, got %d (%s)", rr.Code, rr.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestPVESetupScriptArgumentAlignment(t *testing.T) {
|
|
tempDir := t.TempDir()
|
|
cfg := &config.Config{
|
|
DataPath: tempDir,
|
|
ConfigPath: tempDir,
|
|
}
|
|
|
|
handlers := newTestConfigHandlers(t, cfg)
|
|
|
|
// Use sentinel values to verify fmt.Sprintf argument alignment
|
|
req := httptest.NewRequest(http.MethodGet,
|
|
"/api/setup-script?type=pve&host=http://SENTINEL_HOST:8006&pulse_url=http://SENTINEL_URL:7656&auth_token=deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef", nil)
|
|
rr := httptest.NewRecorder()
|
|
|
|
handlers.HandleSetupScript(rr, req)
|
|
|
|
if rr.Code != http.StatusOK {
|
|
t.Fatalf("expected 200 OK, got %d (%s)", rr.Code, rr.Body.String())
|
|
}
|
|
|
|
script := rr.Body.String()
|
|
|
|
// Critical alignment checks to prevent fmt.Sprintf argument mismatch bugs
|
|
// After refactor: script uses bash variables ($PULSE_URL, $TOKEN_NAME) instead of fmt.Sprintf substitutions
|
|
tests := []struct {
|
|
name string
|
|
contains string
|
|
desc string
|
|
}{
|
|
{
|
|
name: "repair_installer_url",
|
|
contains: `INSTALLER_URL="$PULSE_URL/api/install/install-sensor-proxy.sh"`,
|
|
desc: "Repair block INSTALLER_URL should use $PULSE_URL bash variable",
|
|
},
|
|
{
|
|
name: "repair_ctid_pulse_server",
|
|
contains: `--pulse-server $PULSE_URL`,
|
|
desc: "Repair --ctid --pulse-server should use $PULSE_URL bash variable",
|
|
},
|
|
{
|
|
name: "runtime_auth_token_ssh_config",
|
|
contains: `-H "Authorization: Bearer $AUTH_TOKEN"`,
|
|
desc: "SSH config Authorization header should use runtime $AUTH_TOKEN variable",
|
|
},
|
|
{
|
|
name: "token_id_uses_tokenname",
|
|
contains: `Token ID: $PULSE_TOKEN_ID`,
|
|
desc: "Token ID should use $PULSE_TOKEN_ID bash variable",
|
|
},
|
|
{
|
|
name: "bash_variables_defined",
|
|
contains: `PULSE_URL="http://SENTINEL_URL:7656"`,
|
|
desc: "Bash variable PULSE_URL should be defined at top of script",
|
|
},
|
|
{
|
|
name: "token_name_variable_defined",
|
|
contains: `TOKEN_NAME="pulse-SENTINEL_URL-`,
|
|
desc: "Bash variable TOKEN_NAME should be defined with correct format",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if !containsString(script, tt.contains) {
|
|
t.Errorf("%s\nExpected to find: %s\nIn generated script (first 500 chars):\n%s",
|
|
tt.desc, tt.contains, truncate(script, 500))
|
|
}
|
|
})
|
|
}
|
|
|
|
// Additional check: ensure authToken doesn't appear in --pulse-server flags
|
|
if containsString(script, "--pulse-server deadbeef") {
|
|
t.Error("BUG: authToken appearing in --pulse-server URL (argument misalignment)")
|
|
}
|
|
}
|
|
|
|
func containsString(s, substr string) bool {
|
|
return len(s) >= len(substr) && (s == substr || len(s) > len(substr) &&
|
|
(findSubstring(s, substr) >= 0))
|
|
}
|
|
|
|
func findSubstring(s, substr string) int {
|
|
for i := 0; i <= len(s)-len(substr); i++ {
|
|
if s[i:i+len(substr)] == substr {
|
|
return i
|
|
}
|
|
}
|
|
return -1
|
|
}
|
|
|
|
func truncate(s string, maxLen int) string {
|
|
if len(s) <= maxLen {
|
|
return s
|
|
}
|
|
return s[:maxLen] + "..."
|
|
}
|