feat: safely control Shell Target
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
// Package target defines the application-facing seam implemented by configuration Targets.
|
||||
package target
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"proxyctl/internal/profile"
|
||||
)
|
||||
|
||||
// State is the detected state of a Target's actual configuration.
|
||||
type State string
|
||||
|
||||
const (
|
||||
Enabled State = "Enabled"
|
||||
Disabled State = "Disabled"
|
||||
External State = "External"
|
||||
Drifted State = "Drifted"
|
||||
)
|
||||
|
||||
type Description struct {
|
||||
Name string
|
||||
Summary string
|
||||
Details string
|
||||
}
|
||||
|
||||
type Availability struct {
|
||||
Available bool
|
||||
Evidence string
|
||||
}
|
||||
|
||||
type Inspection struct {
|
||||
State State
|
||||
Evidence string
|
||||
}
|
||||
|
||||
type OperationResult struct {
|
||||
Inspection Inspection
|
||||
Err error
|
||||
}
|
||||
|
||||
// Target hides platform paths and commands from the application and TUI.
|
||||
type Target interface {
|
||||
ID() string
|
||||
Description() Description
|
||||
Availability(context.Context) Availability
|
||||
Inspect(context.Context, *profile.Profile) Inspection
|
||||
SetDesired(context.Context, bool, *profile.Profile) OperationResult
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package target_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"proxyctl/internal/profile"
|
||||
"proxyctl/internal/target"
|
||||
)
|
||||
|
||||
type seamTarget struct{}
|
||||
|
||||
func (seamTarget) ID() string { return "seam" }
|
||||
func (seamTarget) Description() target.Description {
|
||||
return target.Description{Name: "Seam", Summary: "summary", Details: "details"}
|
||||
}
|
||||
func (seamTarget) Availability(context.Context) target.Availability {
|
||||
return target.Availability{Available: true, Evidence: "detected"}
|
||||
}
|
||||
func (seamTarget) Inspect(context.Context, *profile.Profile) target.Inspection {
|
||||
return target.Inspection{State: target.Disabled, Evidence: "absent"}
|
||||
}
|
||||
func (seamTarget) SetDesired(context.Context, bool, *profile.Profile) target.OperationResult {
|
||||
return target.OperationResult{Inspection: target.Inspection{State: target.Enabled}}
|
||||
}
|
||||
|
||||
func TestSharedTargetInterfaceDoesNotExposePlatformDetails(t *testing.T) {
|
||||
var adapter target.Target = seamTarget{}
|
||||
if adapter.ID() != "seam" || !adapter.Availability(context.Background()).Available {
|
||||
t.Fatal("Target seam did not expose expected domain behavior")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
// Package targettest provides the common behavioral contract for real Target adapters.
|
||||
package targettest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"proxyctl/internal/profile"
|
||||
"proxyctl/internal/target"
|
||||
)
|
||||
|
||||
// Fixture supplies adapter-specific host setup while assertions stay at the Target seam.
|
||||
type Fixture struct {
|
||||
Target target.Target
|
||||
Active *profile.Profile
|
||||
MakeExternal func(*testing.T)
|
||||
MakeDrifted func(*testing.T)
|
||||
PermissionFailure func(*testing.T) target.OperationResult
|
||||
PartialFailure func(*testing.T) target.OperationResult
|
||||
RedactionFailure func(*testing.T) target.OperationResult
|
||||
Secret string
|
||||
}
|
||||
|
||||
type Factory func(*testing.T) Fixture
|
||||
|
||||
// Run verifies the state and desired-state behavior shared by every adapter.
|
||||
// Adapter suites add injected permission and partial-effect failures because their
|
||||
// failure injection mechanisms necessarily differ.
|
||||
func Run(t *testing.T, factory Factory) {
|
||||
t.Helper()
|
||||
t.Run("common Target contract", func(t *testing.T) {
|
||||
fixture := factory(t)
|
||||
ctx := context.Background()
|
||||
if !fixture.Target.Availability(ctx).Available {
|
||||
t.Fatal("Target fixture must be available")
|
||||
}
|
||||
if got := fixture.Target.Inspect(ctx, fixture.Active).State; got != target.Disabled {
|
||||
t.Fatalf("initial state = %s, want Disabled", got)
|
||||
}
|
||||
if got := fixture.Target.SetDesired(ctx, true, nil); got.Err == nil || !strings.Contains(got.Err.Error(), "Profile management") {
|
||||
t.Fatalf("nil Active Profile = %#v", got)
|
||||
}
|
||||
invalid := &profile.Profile{Name: "invalid", Mode: profile.SingleMode, SingleURL: "ftp://proxy.example:21"}
|
||||
if got := fixture.Target.SetDesired(ctx, true, invalid); got.Err == nil || got.Inspection.State != target.Disabled {
|
||||
t.Fatalf("validation failure = %#v", got)
|
||||
}
|
||||
if got := fixture.Target.SetDesired(ctx, true, fixture.Active); got.Err != nil || got.Inspection.State != target.Enabled {
|
||||
t.Fatalf("enable = %#v", got)
|
||||
}
|
||||
if got := fixture.Target.SetDesired(ctx, true, fixture.Active); got.Err != nil || got.Inspection.State != target.Enabled {
|
||||
t.Fatalf("idempotent enable = %#v", got)
|
||||
}
|
||||
fixture.MakeDrifted(t)
|
||||
if got := fixture.Target.Inspect(ctx, fixture.Active).State; got != target.Drifted {
|
||||
t.Fatalf("drift state = %s", got)
|
||||
}
|
||||
if got := fixture.Target.SetDesired(ctx, true, fixture.Active); got.Err != nil || got.Inspection.State != target.Enabled {
|
||||
t.Fatalf("repair = %#v", got)
|
||||
}
|
||||
if got := fixture.Target.SetDesired(ctx, false, nil); got.Err != nil {
|
||||
t.Fatalf("disable = %#v", got)
|
||||
}
|
||||
if got := fixture.Target.SetDesired(ctx, false, nil); got.Err != nil {
|
||||
t.Fatalf("idempotent disable = %#v", got)
|
||||
}
|
||||
|
||||
external := factory(t)
|
||||
external.MakeExternal(t)
|
||||
if got := external.Target.Inspect(ctx, external.Active).State; got != target.External {
|
||||
t.Fatalf("external state = %s", got)
|
||||
}
|
||||
if got := external.Target.SetDesired(ctx, true, external.Active); got.Err != nil {
|
||||
t.Fatalf("enable over External = %#v", got)
|
||||
}
|
||||
if got := external.Target.SetDesired(ctx, false, nil); got.Err != nil || got.Inspection.State != target.External {
|
||||
t.Fatalf("restore External = %#v", got)
|
||||
}
|
||||
|
||||
permission := fixture.PermissionFailure(t)
|
||||
if permission.Err == nil || !errors.Is(permission.Err, os.ErrPermission) || permission.Inspection.State != target.Disabled {
|
||||
t.Fatalf("permission failure = %#v", permission)
|
||||
}
|
||||
partial := fixture.PartialFailure(t)
|
||||
if partial.Err == nil || partial.Inspection.State != target.External {
|
||||
t.Fatalf("partial failure = %#v", partial)
|
||||
}
|
||||
redacted := fixture.RedactionFailure(t)
|
||||
if redacted.Err == nil || fixture.Secret == "" || strings.Contains(redacted.Err.Error(), fixture.Secret) {
|
||||
t.Fatalf("secret was not redacted: %#v", redacted)
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user