mirror of
https://github.com/kernelkit/infix.git
synced 2026-08-06 07:33:01 +02:00
Implements the configure accordion section with a candidate datastore workflow: opening Configure copies running → candidate (Enter), each card saves directly to candidate, Apply copies candidate → running atomically, and Abort discards by resetting candidate from running. - RESTCONF client: add Put, Patch, Delete, GetDatastore, PutDatastore, CopyDatastore, and a shared doRequest/writeJSON helper - Handlers: ConfigureHandler (enter/apply/abort), ConfigureSystemHandler (hostname, clock, NTP, DNS, motd, editor), ConfigureUsersHandler (add/delete user, shell, password, SSH keys, NACM group membership) - Status > NACM page: read-only group permission matrix derived from ietf-netconf-acm groups + rule-list mappings - Sticky Apply/Abort toolbar with custom confirm dialog; server returns HX-Redirect:/ so HTMX does a full-page reload, keeping sidebar in sync - Accordion persistence fixed: Configure excluded from localStorage so it never auto-reopens after Abort/Apply; restore no longer closes an accordion that contains the active page - Password hashing by shelling out to mkpasswd(1) Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
110 lines
2.2 KiB
Go
110 lines
2.2 KiB
Go
// SPDX-License-Identifier: MIT
|
|
|
|
package testutil
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"sync"
|
|
)
|
|
|
|
type mockEntry struct {
|
|
body any
|
|
err error
|
|
}
|
|
|
|
type MockFetcher struct {
|
|
mu sync.Mutex
|
|
responses map[string]mockEntry
|
|
errors map[string]error
|
|
}
|
|
|
|
func NewMockFetcher() *MockFetcher {
|
|
return &MockFetcher{
|
|
responses: make(map[string]mockEntry),
|
|
errors: make(map[string]error),
|
|
}
|
|
}
|
|
|
|
func (m *MockFetcher) SetResponse(path string, body any) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
m.responses[path] = mockEntry{body: body}
|
|
}
|
|
|
|
func (m *MockFetcher) SetError(path string, err error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
m.errors[path] = err
|
|
}
|
|
|
|
func (m *MockFetcher) Get(_ context.Context, path string, target any) error {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
|
|
if err, ok := m.errors[path]; ok {
|
|
return err
|
|
}
|
|
|
|
entry, ok := m.responses[path]
|
|
if !ok {
|
|
return nil
|
|
}
|
|
|
|
raw, err := json.Marshal(entry.body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return json.Unmarshal(raw, target)
|
|
}
|
|
|
|
func (m *MockFetcher) GetRaw(_ context.Context, path string) ([]byte, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
|
|
if err, ok := m.errors[path]; ok {
|
|
return nil, err
|
|
}
|
|
|
|
entry, ok := m.responses[path]
|
|
if !ok {
|
|
return []byte("{}"), nil
|
|
}
|
|
|
|
return json.Marshal(entry.body)
|
|
}
|
|
|
|
func (m *MockFetcher) Post(_ context.Context, path string) error {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
|
|
if err, ok := m.errors[path]; ok {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (m *MockFetcher) PostJSON(_ context.Context, path string, _ any) error {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
|
|
if err, ok := m.errors[path]; ok {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (m *MockFetcher) Put(_ context.Context, _ string, _ any) error { return nil }
|
|
func (m *MockFetcher) Patch(_ context.Context, _ string, _ any) error { return nil }
|
|
func (m *MockFetcher) Delete(_ context.Context, _ string) error { return nil }
|
|
|
|
func (m *MockFetcher) GetDatastore(_ context.Context, _ string) (json.RawMessage, error) {
|
|
return json.RawMessage("{}"), nil
|
|
}
|
|
|
|
func (m *MockFetcher) PutDatastore(_ context.Context, _ string, _ json.RawMessage) error {
|
|
return nil
|
|
}
|
|
|
|
func (m *MockFetcher) CopyDatastore(_ context.Context, _, _ string) error { return nil }
|