refactor: move V1 code into v1/ subdirectory

- git mv cmd/ internal/ pkg/ test/ go.mod go.sum Makefile build.sh docs/ v1/
- Create v1/README.md with V1 documentation
- Update root README for V1 + V2 structure
- V1 still builds (cd v1 && go build ./cmd/hostkeeper) and 105 tests pass
- Root is now clean for V2 development
This commit is contained in:
swanadiva
2026-07-07 11:56:27 +07:00
parent 8ebdebedc8
commit 847989df75
68 changed files with 58 additions and 116 deletions
+142
View File
@@ -0,0 +1,142 @@
package errors_test
import (
stderrors "errors"
"strings"
"testing"
apperrors "git.tukangketik.id/swanadiva/hostkeeper/internal/errors"
)
func TestAppError(t *testing.T) {
cause := stderrors.New("underlying issue")
appErr := apperrors.NewAppError(
apperrors.ErrAuthFailed,
"Authentication failed",
cause,
[]string{"Check credentials", "Verify key permissions"},
)
if appErr.Code != apperrors.ErrAuthFailed {
t.Errorf("Expected code '%s', got '%s'", apperrors.ErrAuthFailed, appErr.Code)
}
if len(appErr.Hints) != 2 {
t.Errorf("Expected 2 hints, got %d", len(appErr.Hints))
}
// Test error message is non-empty
if appErr.Error() == "" {
t.Error("Expected non-empty error message")
}
// Test Unwrap
if !stderrors.Is(appErr, cause) {
t.Error("Expected errors.Is to match the cause via Unwrap")
}
}
func TestConnectionError(t *testing.T) {
connErr := apperrors.NewConnectionError(
"auth",
"Authentication failed",
"ssh: handshake failed",
[]string{"Check credentials", "Verify key permissions"},
)
if connErr.Type != "auth" {
t.Errorf("Expected type 'auth', got '%s'", connErr.Type)
}
if len(connErr.Hints) != 2 {
t.Errorf("Expected 2 hints, got %d", len(connErr.Hints))
}
if connErr.Error() == "" {
t.Error("Expected non-empty error message")
}
}
func TestHandleSSHError(t *testing.T) {
tests := []struct {
name string
inputErr error
expectedType string
}{
{
name: "connection refused",
inputErr: stderrors.New("dial tcp: connection refused"),
expectedType: "network",
},
{
name: "authentication failed",
inputErr: stderrors.New("ssh: handshake failed: ssh: unable to authenticate"),
expectedType: "auth",
},
{
name: "timeout",
inputErr: stderrors.New("dial tcp: connection timed out"),
expectedType: "timeout",
},
{
name: "no such host",
inputErr: stderrors.New("dial tcp: lookup: no such host"),
expectedType: "config",
},
{
name: "permission denied",
inputErr: stderrors.New("ssh: permission denied"),
expectedType: "auth",
},
{
name: "unknown error",
inputErr: stderrors.New("something went wrong"),
expectedType: "unknown",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
connErr := apperrors.HandleSSHError(tt.inputErr)
if connErr == nil {
t.Fatal("Expected non-nil ConnectionError")
}
if connErr.Type != tt.expectedType {
t.Errorf("Expected type '%s', got '%s'", tt.expectedType, connErr.Type)
}
if len(connErr.Hints) == 0 {
t.Error("Expected at least one hint")
}
})
}
}
func TestHandleSSHErrorNil(t *testing.T) {
result := apperrors.HandleSSHError(nil)
if result != nil {
t.Error("Expected nil for nil input")
}
}
func TestFormatConnectionError(t *testing.T) {
connErr := apperrors.NewConnectionError(
"auth",
"Authentication failed",
"ssh: handshake failed",
[]string{"Check credentials", "Verify key permissions"},
)
output := apperrors.FormatConnectionError(connErr)
if output == "" {
t.Error("Expected non-empty formatted output")
}
if !strings.Contains(output, "Authentication failed") {
t.Error("Expected output to contain error message")
}
if !strings.Contains(output, "Possible solutions") {
t.Error("Expected output to contain hints section")
}
}