Skip to content

chore: add code coverage to orb backends #90

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
202 changes: 202 additions & 0 deletions agent/backend/devicediscovery/device_discovery_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
package devicediscovery_test

import (
"context"
"encoding/json"
"log/slog"
"net/http"
"net/http/httptest"
"net/url"
"os"
"strings"
"testing"

"github.com/stretchr/testify/assert"

"github.com/netboxlabs/orb-agent/agent/backend"
"github.com/netboxlabs/orb-agent/agent/backend/devicediscovery"
"github.com/netboxlabs/orb-agent/agent/backend/mocks"
"github.com/netboxlabs/orb-agent/agent/config"
"github.com/netboxlabs/orb-agent/agent/policies"
)

type StatusResponse struct {
StartTime string `json:"start_time"`
Version string `json:"version"`
UpTime float64 `json:"up_time"`
}

func TestDeviceDiscoveryBackendStart(t *testing.T) {
// Create server
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")

if r.URL.Path == "/api/v1/status" {
response := StatusResponse{
Version: "1.3.4",
StartTime: "2023-10-01T12:00:00Z",
UpTime: 123.456,
}
w.WriteHeader(http.StatusOK)
err := json.NewEncoder(w).Encode(response)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
} else if r.URL.Path == "/api/v1/capabilities" {
capabilities := map[string]any{
"capability": true,
}
w.WriteHeader(http.StatusOK)
err := json.NewEncoder(w).Encode(capabilities)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
} else if strings.Contains(r.URL.Path, "/api/v1/policies") {
if r.Method == http.MethodPost {
w.WriteHeader(http.StatusOK)
response := map[string]any{
"status": "success",
"message": "Policy applied successfully",
}
err := json.NewEncoder(w).Encode(response)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
} else if r.Method == http.MethodDelete {
w.WriteHeader(http.StatusOK)
response := map[string]any{
"status": "success",
"message": "Policy removed successfully",
}
err := json.NewEncoder(w).Encode(response)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
} else {
w.WriteHeader(http.StatusNotFound)
}
}))
defer server.Close()

// Parse server URL
serverURL, err := url.Parse(server.URL)
assert.NoError(t, err)

// Create logger
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))

// Create a mock repository
repo, err := policies.NewMemRepo()
assert.NoError(t, err)

// Create a mock command
mockCmd := &mocks.MockCmd{}
mocks.SetupSuccessfulProcess(mockCmd, 12345)

// Save original function and restore after test
originalNewCmdOptions := backend.NewCmdOptions
defer func() {
backend.NewCmdOptions = originalNewCmdOptions
}()

// Override NewCmdOptions to return our mock
backend.NewCmdOptions = func(options backend.CmdOptions, name string, args ...string) backend.Commander {
// Assert that the correct parameters were passed
assert.Equal(t, "device-discovery", name, "Expected command name to be device-discovery")
assert.Contains(t, args, "--port", "Expected args to contain port")
assert.Contains(t, args, "--host", "Expected args to contain host")
assert.False(t, options.Buffered, "Expected buffered to be false")
assert.True(t, options.Streaming, "Expected streaming to be true")
return mockCmd
}

assert.True(t, devicediscovery.Register(), "Failed to register DeviceDiscovery backend")

assert.True(t, backend.HaveBackend("device_discovery"), "Failed to get DeviceDiscovery backend")

be := backend.GetBackend("device_discovery")

assert.Equal(t, backend.Unknown, be.GetInitialState())

// Configure backend
err = be.Configure(logger, repo, map[string]any{
"host": serverURL.Hostname(),
"port": serverURL.Port(),
}, config.BackendCommons{})
assert.NoError(t, err)

// Start the backend
ctx, cancel := context.WithCancel(context.Background())
err = be.Start(ctx, cancel)

// Assert successful start
assert.NoError(t, err)

// Get Running status
status, _, err := be.GetRunningStatus()
assert.NoError(t, err)
assert.Equal(t, backend.Running, status, "Expected backend to be running")

// Get capabilities
capabilities, err := be.GetCapabilities()
assert.NoError(t, err)
assert.Equal(t, capabilities["capability"], true, "Expected capability to be true")

data := policies.PolicyData{
ID: "dummy-policy-id",
Name: "dummy-policy-name",
Data: map[string]any{"key": "value"},
}
// Apply policy
err = be.ApplyPolicy(data, false)
assert.NoError(t, err)

// Update policy
err = be.ApplyPolicy(data, true)
assert.NoError(t, err)

// Assert restart
err = be.FullReset(ctx)
assert.NoError(t, err)

// Verify expectations
mockCmd.AssertExpectations(t)
}

func TestDeviceDiscoveryBackendCompleted(t *testing.T) {
// Create a mock command that simulates a failure
mockCmd := &mocks.MockCmd{}
mocks.SetupCompletedProcess(mockCmd, 0, nil)
// Save original function and restore after test
originalNewCmdOptions := backend.NewCmdOptions
defer func() {
backend.NewCmdOptions = originalNewCmdOptions
}()

// Override NewCmdOptions to return our mock
backend.NewCmdOptions = func(_ backend.CmdOptions, _ string, _ ...string) backend.Commander {
return mockCmd
}

assert.True(t, devicediscovery.Register(), "Failed to register DeviceDiscovery backend")

assert.True(t, backend.HaveBackend("device_discovery"), "Failed to get DeviceDiscovery backend")

be := backend.GetBackend("device_discovery")

// Configure backend with invalid parameters
err := be.Configure(slog.Default(), nil, map[string]any{
"host": "invalid-host",
}, config.BackendCommons{})
assert.NoError(t, err)

ctx, cancel := context.WithCancel(context.Background())
err = be.Start(ctx, cancel)

assert.Error(t, err)
}
3 changes: 3 additions & 0 deletions agent/backend/mocks/mocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ func SetupSuccessfulProcess(mockCmd *MockCmd, pid int) (<-chan string, <-chan st
// Delay before sending to simulate process startup
time.Sleep(10 * time.Millisecond)
statusCh <- status
// Simulate some output
stdoutCh <- "success"
stderrCh <- "error"
}()

return readOnlyStdoutCh, readOnlyStderrCh
Expand Down
Loading