diff --git a/internal/drivers/delegation.go b/internal/drivers/delegation.go index cdcab3b..498fd86 100644 --- a/internal/drivers/delegation.go +++ b/internal/drivers/delegation.go @@ -9,14 +9,14 @@ import ( "sort" "strings" - "github.com/GoCodeAlone/workflow-plugin-hover/internal/hover" + "github.com/GoCodeAlone/workflow-plugin-hover/pkg/hoverclient" "github.com/GoCodeAlone/workflow/interfaces" ) -// HoverDelegationClient is the subset of *hover.Client that DelegationDriver +// HoverDelegationClient is the subset of *hoverclient.Client that DelegationDriver // depends on. Injectable for tests. type HoverDelegationClient interface { - GetDomainDelegation(ctx context.Context, domain string) (*hover.DomainDelegation, error) + GetDomainDelegation(ctx context.Context, domain string) (*hoverclient.DomainDelegation, error) SetNameservers(ctx context.Context, domain string, ns []string) error } @@ -26,15 +26,15 @@ type HoverDelegationClient interface { // ProviderID = apex domain name (e.g. "example.com"). One resource = one // domain. Outputs contain only the desired nameservers as []any // (structpb-safe). v0.2.0 ships Delete = reset to Hover defaults -// [ns1.hover.com, ns2.hover.com]; restore-from-stash is deferred to +// [ns1.hoverclient.com, ns2.hoverclient.com]; restore-from-stash is deferred to // v0.3.0 because interfaces.ResourceRef has no state channel. type DelegationDriver struct { client HoverDelegationClient nsResolver func(context.Context, string) ([]string, error) } -// NewDelegationDriver returns a DelegationDriver bound to a real *hover.Client. -func NewDelegationDriver(c *hover.Client) *DelegationDriver { +// NewDelegationDriver returns a DelegationDriver bound to a real *hoverclient.Client. +func NewDelegationDriver(c *hoverclient.Client) *DelegationDriver { return &DelegationDriver{client: c, nsResolver: lookupPublicNameservers} } @@ -238,7 +238,7 @@ func (d *DelegationDriver) Update(ctx context.Context, ref interfaces.ResourceRe // hoverDefaultNameservers is the Delete target for v0.2.0 (per A5). // ResourceRef has no state channel for previous_nameservers restore; // that enhancement is v0.3.0 follow-up territory. -var hoverDefaultNameservers = []string{"ns1.hover.com", "ns2.hover.com"} +var hoverDefaultNameservers = []string{"ns1.hoverclient.com", "ns2.hoverclient.com"} // Delete resets the registrar nameservers to Hover's defaults. // Operators whose domains had non-default originals must restore diff --git a/internal/drivers/delegation_test.go b/internal/drivers/delegation_test.go index 1c7f22f..fc95736 100644 --- a/internal/drivers/delegation_test.go +++ b/internal/drivers/delegation_test.go @@ -6,19 +6,19 @@ import ( "fmt" "testing" - "github.com/GoCodeAlone/workflow-plugin-hover/internal/hover" + "github.com/GoCodeAlone/workflow-plugin-hover/pkg/hoverclient" "github.com/GoCodeAlone/workflow/interfaces" ) type fakeDelegationClient struct { - getResult *hover.DomainDelegation + getResult *hoverclient.DomainDelegation getErr error getCalls int setErr error lastSetNS []string } -func (f *fakeDelegationClient) GetDomainDelegation(_ context.Context, _ string) (*hover.DomainDelegation, error) { +func (f *fakeDelegationClient) GetDomainDelegation(_ context.Context, _ string) (*hoverclient.DomainDelegation, error) { f.getCalls++ return f.getResult, f.getErr } @@ -122,7 +122,7 @@ func TestDelegationDriver_Create_DuplicateNameservers_Rejected(t *testing.T) { func TestDelegationDriver_Read_HappyPath(t *testing.T) { fc := &fakeDelegationClient{ - getResult: &hover.DomainDelegation{ + getResult: &hoverclient.DomainDelegation{ ID: "domain-example.com", Name: "example.com", Nameservers: []string{"ns1.do.com", "ns2.do.com"}, @@ -196,7 +196,7 @@ func TestDelegationDriver_Read_PropagatesError(t *testing.T) { func TestDelegationDriver_Update_HappyPath(t *testing.T) { fc := &fakeDelegationClient{ - getResult: &hover.DomainDelegation{ + getResult: &hoverclient.DomainDelegation{ ID: "domain-example.com", Name: "example.com", Nameservers: []string{"ns1.do.com", "ns2.do.com"}, }, @@ -256,8 +256,8 @@ func TestDelegationDriver_Delete_ResetsToHoverDefaults(t *testing.T) { if err := d.Delete(context.Background(), ref); err != nil { t.Fatalf("Delete: %v", err) } - if len(fc.lastSetNS) != 2 || fc.lastSetNS[0] != "ns1.hover.com" || fc.lastSetNS[1] != "ns2.hover.com" { - t.Errorf("Delete set NS = %v, want [ns1.hover.com ns2.hover.com]", fc.lastSetNS) + if len(fc.lastSetNS) != 2 || fc.lastSetNS[0] != "ns1.hoverclient.com" || fc.lastSetNS[1] != "ns2.hoverclient.com" { + t.Errorf("Delete set NS = %v, want [ns1.hoverclient.com ns2.hoverclient.com]", fc.lastSetNS) } } @@ -380,7 +380,7 @@ func TestDelegationDriver_Diff_DomainChange_NeedsReplace(t *testing.T) { func TestDelegationDriver_HealthCheck_Healthy(t *testing.T) { fc := &fakeDelegationClient{ - getResult: &hover.DomainDelegation{ + getResult: &hoverclient.DomainDelegation{ ID: "domain-example.com", Name: "example.com", Nameservers: []string{"a.com", "b.com"}, }, @@ -449,20 +449,20 @@ func TestDelegationDriver_CtxCanceled_AllMethods(t *testing.T) { } func TestDelegationDriver_Read_PropagatesErrEmptyNameservers(t *testing.T) { - // Callers using errors.Is(driverErr, hover.ErrEmptyNameservers) to + // Callers using errors.Is(driverErr, hoverclient.ErrEmptyNameservers) to // distinguish "Hover surfaced 0 nameservers" from other failures need // the sentinel to survive the driver's error wrap. This test defends // that contract. fc := &fakeDelegationClient{ - getErr: fmt.Errorf("hover: GetDomainDelegation %q: %w", "example.com", hover.ErrEmptyNameservers), + getErr: fmt.Errorf("hover: GetDomainDelegation %q: %w", "example.com", hoverclient.ErrEmptyNameservers), } d := NewDelegationDriverWithClient(fc) _, err := d.Read(context.Background(), interfaces.ResourceRef{Name: "example.com", ProviderID: "example.com"}) if err == nil { t.Fatal("expected error") } - if !errors.Is(err, hover.ErrEmptyNameservers) { - t.Errorf("errors.Is should match hover.ErrEmptyNameservers through driver wrap; got %v", err) + if !errors.Is(err, hoverclient.ErrEmptyNameservers) { + t.Errorf("errors.Is should match hoverclient.ErrEmptyNameservers through driver wrap; got %v", err) } } diff --git a/internal/drivers/dns.go b/internal/drivers/dns.go index 3d9831c..0fe4aa3 100644 --- a/internal/drivers/dns.go +++ b/internal/drivers/dns.go @@ -3,7 +3,7 @@ // // Hover exposes NO official API. All endpoints below are derived from // https://github.com/pjslauta/hover-dyn-dns and browser traffic inspection. -// Endpoint inventory (all relative to https://www.hover.com): +// Endpoint inventory (all relative to https://www.hoverclient.com): // // GET /api/domains//dns — list records for a zone // POST /api/dns — create a record (form: domain_id, name, type, content, ttl) @@ -20,16 +20,16 @@ import ( "fmt" "strings" - "github.com/GoCodeAlone/workflow-plugin-hover/internal/hover" + "github.com/GoCodeAlone/workflow-plugin-hover/pkg/hoverclient" "github.com/GoCodeAlone/workflow/interfaces" ) -// HoverDNSClient is the subset of hover.Client used by DNSDriver (injectable for tests). +// HoverDNSClient is the subset of hoverclient.Client used by DNSDriver (injectable for tests). type HoverDNSClient interface { - GetDomain(ctx context.Context, domain string) (*hover.Domain, error) - ListRecords(ctx context.Context, domain string) ([]hover.DNSRecord, error) - CreateRecord(ctx context.Context, domainID string, rec hover.DNSRecord) (*hover.DNSRecord, error) - UpdateRecord(ctx context.Context, recordID string, rec hover.DNSRecord) error + GetDomain(ctx context.Context, domain string) (*hoverclient.Domain, error) + ListRecords(ctx context.Context, domain string) ([]hoverclient.DNSRecord, error) + CreateRecord(ctx context.Context, domainID string, rec hoverclient.DNSRecord) (*hoverclient.DNSRecord, error) + UpdateRecord(ctx context.Context, recordID string, rec hoverclient.DNSRecord) error DeleteRecord(ctx context.Context, recordID string) error } @@ -39,8 +39,8 @@ type DNSDriver struct { client HoverDNSClient } -// NewDNSDriver creates a DNSDriver backed by a real hover.Client. -func NewDNSDriver(c *hover.Client) *DNSDriver { +// NewDNSDriver creates a DNSDriver backed by a real hoverclient.Client. +func NewDNSDriver(c *hoverclient.Client) *DNSDriver { return &DNSDriver{client: c} } @@ -158,7 +158,7 @@ func (d *DNSDriver) Diff(_ context.Context, desired interfaces.ResourceSpec, cur // slice and matched against desired records by exact Content // (and TTL when specified) rather than by slice position. Each // match consumes one candidate to preserve multiset semantics. - currentByKey := make(map[string][]hover.DNSRecord) + currentByKey := make(map[string][]hoverclient.DNSRecord) for _, r := range currentRecs { key := recordKey(r.Type, r.Name) currentByKey[key] = append(currentByKey[key], r) @@ -231,7 +231,7 @@ func (d *DNSDriver) readOutput(ctx context.Context, domain, name string) (*inter return dnsOutput(domain, name, recs), nil } -func (d *DNSDriver) upsertRecords(ctx context.Context, domain string, desired []hover.DNSRecord) error { +func (d *DNSDriver) upsertRecords(ctx context.Context, domain string, desired []hoverclient.DNSRecord) error { // An empty desired set means "drop everything" — fall through into // the prune step rather than short-circuiting. Without this, an // explicit `records: []` would still leave upstream records intact. @@ -245,7 +245,7 @@ func (d *DNSDriver) upsertRecords(ctx context.Context, domain string, desired [] return fmt.Errorf("hover dns resolve domain %q: %w", domain, err) } - existingByKey := make(map[string][]hover.DNSRecord) + existingByKey := make(map[string][]hoverclient.DNSRecord) for _, r := range dom.Records { key := recordKey(r.Type, r.Name) existingByKey[key] = append(existingByKey[key], r) @@ -264,7 +264,7 @@ func (d *DNSDriver) upsertRecords(ctx context.Context, domain string, desired [] // a transient state with two identical records. Diff would still // converge eventually, but apply would do unnecessary writes and // could fail under multi-record configs. - deferredUpdates := make([]hover.DNSRecord, 0, len(desired)) + deferredUpdates := make([]hoverclient.DNSRecord, 0, len(desired)) for _, dr := range desired { key := recordKey(dr.Type, dr.Name) candidates := existingByKey[key] @@ -351,7 +351,7 @@ func domainFromConfigIfPresent(config map[string]any) (string, bool, error) { return s, true, nil } -// declaredRecords parses config["records"] into a []hover.DNSRecord slice. +// declaredRecords parses config["records"] into a []hoverclient.DNSRecord slice. // // `records` is REQUIRED. A missing key errors out — silently coercing // to an empty slice would let upsertRecords prune every upstream @@ -359,7 +359,7 @@ func domainFromConfigIfPresent(config map[string]any) (string, bool, error) { // to set the key. An explicitly empty `records: []` IS allowed (and // does deliberately prune everything); only the missing-key / // wrong-type cases are rejected. -func declaredRecords(config map[string]any) ([]hover.DNSRecord, error) { +func declaredRecords(config map[string]any) ([]hoverclient.DNSRecord, error) { raw, present := config["records"] if !present { return nil, fmt.Errorf("hover dns: config.records is required (use an explicit 'records: []' to drop every record)") @@ -368,7 +368,7 @@ func declaredRecords(config map[string]any) ([]hover.DNSRecord, error) { if err != nil { return nil, fmt.Errorf("hover dns: config.records: %w", err) } - out := make([]hover.DNSRecord, 0, len(items)) + out := make([]hoverclient.DNSRecord, 0, len(items)) for i, m := range items { rec, err := recordFromMap(i, m) if err != nil { @@ -398,24 +398,24 @@ func toSliceOfMaps(v any) ([]map[string]any, error) { } } -func recordFromMap(index int, m map[string]any) (hover.DNSRecord, error) { +func recordFromMap(index int, m map[string]any) (hoverclient.DNSRecord, error) { typ, err := requiredString(m, "type", index) if err != nil { - return hover.DNSRecord{}, err + return hoverclient.DNSRecord{}, err } name, err := requiredString(m, "name", index) if err != nil { - return hover.DNSRecord{}, err + return hoverclient.DNSRecord{}, err } content, err := requiredString(m, "content", index) if err != nil { - return hover.DNSRecord{}, err + return hoverclient.DNSRecord{}, err } ttl, err := optionalNonNegativeInt(m, "ttl", index) if err != nil { - return hover.DNSRecord{}, err + return hoverclient.DNSRecord{}, err } - return hover.DNSRecord{ + return hoverclient.DNSRecord{ Type: strings.ToUpper(typ), Name: name, Content: content, @@ -487,8 +487,8 @@ func recordKey(typ, name string) string { } // dnsRecordsFromOutput deserialises the "records" key from a ResourceOutput -// Outputs map back into []hover.DNSRecord for diffing. -func dnsRecordsFromOutput(out *interfaces.ResourceOutput) ([]hover.DNSRecord, error) { +// Outputs map back into []hoverclient.DNSRecord for diffing. +func dnsRecordsFromOutput(out *interfaces.ResourceOutput) ([]hoverclient.DNSRecord, error) { if out == nil || out.Outputs == nil { return nil, nil } @@ -500,7 +500,7 @@ func dnsRecordsFromOutput(out *interfaces.ResourceOutput) ([]hover.DNSRecord, er if err != nil { return nil, fmt.Errorf("hover dns: outputs.records: %w", err) } - recs := make([]hover.DNSRecord, 0, len(items)) + recs := make([]hoverclient.DNSRecord, 0, len(items)) for _, m := range items { typ, _ := m["type"].(string) name, _ := m["name"].(string) @@ -510,7 +510,7 @@ func dnsRecordsFromOutput(out *interfaces.ResourceOutput) ([]hover.DNSRecord, er if typ == "" || name == "" { continue } - recs = append(recs, hover.DNSRecord{ + recs = append(recs, hoverclient.DNSRecord{ ID: id, Type: typ, Name: name, @@ -524,7 +524,7 @@ func dnsRecordsFromOutput(out *interfaces.ResourceOutput) ([]hover.DNSRecord, er // dnsOutput builds the structpb-safe ResourceOutput for a Hover DNS zone. // All Outputs values are primitive leaves (string/int/bool) — no typed slices. // Records are encoded as []map[string]any per the structpb-boundary invariant. -func dnsOutput(domain, name string, records []hover.DNSRecord) *interfaces.ResourceOutput { +func dnsOutput(domain, name string, records []hoverclient.DNSRecord) *interfaces.ResourceOutput { outputs := map[string]any{ "domain": domain, } diff --git a/internal/drivers/dns_test.go b/internal/drivers/dns_test.go index 54c3ea2..68ac82d 100644 --- a/internal/drivers/dns_test.go +++ b/internal/drivers/dns_test.go @@ -6,14 +6,14 @@ import ( "fmt" "testing" - "github.com/GoCodeAlone/workflow-plugin-hover/internal/hover" + "github.com/GoCodeAlone/workflow-plugin-hover/pkg/hoverclient" "github.com/GoCodeAlone/workflow/interfaces" ) // fakeClient is a test double for HoverDNSClient. type fakeClient struct { domainID string // hover-assigned domain ID returned by GetDomain - records []hover.DNSRecord + records []hoverclient.DNSRecord createErr error updateErr error deleteErr error @@ -23,7 +23,7 @@ type fakeClient struct { lastCreateDomainID string // captured for assertions } -func (f *fakeClient) GetDomain(_ context.Context, domain string) (*hover.Domain, error) { +func (f *fakeClient) GetDomain(_ context.Context, domain string) (*hoverclient.Domain, error) { if f.listErr != nil { return nil, f.listErr } @@ -31,21 +31,21 @@ func (f *fakeClient) GetDomain(_ context.Context, domain string) (*hover.Domain, if id == "" { id = "dom1" } - recs := make([]hover.DNSRecord, len(f.records)) + recs := make([]hoverclient.DNSRecord, len(f.records)) copy(recs, f.records) - return &hover.Domain{ID: id, Name: domain, Records: recs}, nil + return &hoverclient.Domain{ID: id, Name: domain, Records: recs}, nil } -func (f *fakeClient) ListRecords(_ context.Context, _ string) ([]hover.DNSRecord, error) { +func (f *fakeClient) ListRecords(_ context.Context, _ string) ([]hoverclient.DNSRecord, error) { if f.listErr != nil { return nil, f.listErr } - out := make([]hover.DNSRecord, len(f.records)) + out := make([]hoverclient.DNSRecord, len(f.records)) copy(out, f.records) return out, nil } -func (f *fakeClient) CreateRecord(_ context.Context, domainID string, rec hover.DNSRecord) (*hover.DNSRecord, error) { +func (f *fakeClient) CreateRecord(_ context.Context, domainID string, rec hoverclient.DNSRecord) (*hoverclient.DNSRecord, error) { if f.createErr != nil { return nil, f.createErr } @@ -57,7 +57,7 @@ func (f *fakeClient) CreateRecord(_ context.Context, domainID string, rec hover. return &cp, nil } -func (f *fakeClient) UpdateRecord(_ context.Context, id string, rec hover.DNSRecord) error { +func (f *fakeClient) UpdateRecord(_ context.Context, id string, rec hoverclient.DNSRecord) error { if f.updateErr != nil { return f.updateErr } @@ -86,7 +86,7 @@ func (f *fakeClient) DeleteRecord(_ context.Context, id string) error { return fmt.Errorf("record %q not found", id) } -func newDriver(records ...hover.DNSRecord) (*DNSDriver, *fakeClient) { +func newDriver(records ...hoverclient.DNSRecord) (*DNSDriver, *fakeClient) { fc := &fakeClient{records: records} return NewDNSDriverWithClient(fc), fc } @@ -138,7 +138,7 @@ func TestDNSDriver_Create_WithRecords(t *testing.T) { } func TestDNSDriver_Create_UpdatesExistingRecord(t *testing.T) { - existing := hover.DNSRecord{ID: "r1", Type: "A", Name: "@", Content: "1.1.1.1", TTL: 300} + existing := hoverclient.DNSRecord{ID: "r1", Type: "A", Name: "@", Content: "1.1.1.1", TTL: 300} d, fc := newDriver(existing) spec := interfaces.ResourceSpec{ @@ -278,7 +278,7 @@ func TestDNSDriver_Update_DomainRenameRejected(t *testing.T) { } func TestDNSDriver_Delete_NoOp(t *testing.T) { - d, _ := newDriver(hover.DNSRecord{ID: "r1", Type: "A", Name: "@", Content: "1.1.1.1"}) + d, _ := newDriver(hoverclient.DNSRecord{ID: "r1", Type: "A", Name: "@", Content: "1.1.1.1"}) err := d.Delete(context.Background(), interfaces.ResourceRef{Name: "example.com", ProviderID: "example.com"}) if err != nil { t.Fatalf("Delete: %v", err) @@ -342,11 +342,11 @@ func TestDeclaredRecords_MissingType(t *testing.T) { } func TestDNSOutput_Structpb(t *testing.T) { - records := []hover.DNSRecord{ + records := []hoverclient.DNSRecord{ {ID: "r1", Type: "A", Name: "@", Content: "1.2.3.4", TTL: 300}, } out := dnsOutput("example.com", "my-zone", records) - // outputs["records"] must be []any, not []hover.DNSRecord, + // outputs["records"] must be []any, not []hoverclient.DNSRecord, // to be structpb-safe. recs, ok := out.Outputs["records"].([]any) if !ok { @@ -560,7 +560,7 @@ func TestDiff_EmptyDesired_WithCurrentRecords_NeedsUpdate(t *testing.T) { // removed records as orphans upstream. func TestUpsertRecords_PrunesExtraRecords(t *testing.T) { fc := &fakeClient{ - records: []hover.DNSRecord{ + records: []hoverclient.DNSRecord{ {ID: "r1", Type: "A", Name: "@", Content: "1.1.1.1"}, {ID: "r2", Type: "A", Name: "www", Content: "1.1.1.1"}, // orphan }, @@ -587,7 +587,7 @@ func TestUpsertRecords_PrunesExtraRecords(t *testing.T) { func TestUpsertRecords_EmptyDesiredDeletesAll(t *testing.T) { fc := &fakeClient{ - records: []hover.DNSRecord{ + records: []hoverclient.DNSRecord{ {ID: "r1", Type: "A", Name: "@", Content: "1.1.1.1"}, {ID: "r2", Type: "A", Name: "www", Content: "1.1.1.1"}, }, diff --git a/internal/provider.go b/internal/provider.go index 2310a27..5c5afd5 100644 --- a/internal/provider.go +++ b/internal/provider.go @@ -11,7 +11,7 @@ import ( "time" "github.com/GoCodeAlone/workflow-plugin-hover/internal/drivers" - "github.com/GoCodeAlone/workflow-plugin-hover/internal/hover" + "github.com/GoCodeAlone/workflow-plugin-hover/pkg/hoverclient" "github.com/GoCodeAlone/workflow/interfaces" "github.com/GoCodeAlone/workflow/platform" ) @@ -24,7 +24,7 @@ var Version = "0.0.0" // - infra.dns — DNS records within Hover's nameservers. // - infra.dns_delegation — registrar-level nameserver delegation. type HoverProvider struct { - client *hover.Client + client *hoverclient.Client drivers map[string]interfaces.ResourceDriver } @@ -58,21 +58,21 @@ func (p *HoverProvider) Initialize(ctx context.Context, config map[string]any) e return fmt.Errorf("hover: missing required config key 'password'") } - var totpSecret hover.TOTPSecret + var totpSecret hoverclient.TOTPSecret if totpRaw != "" { - ts, err := hover.ParseBase32(totpRaw) + ts, err := hoverclient.ParseBase32(totpRaw) if err != nil { return fmt.Errorf("hover: invalid totp_secret: %w", err) } totpSecret = ts } - creds := hover.Credentials{ + creds := hoverclient.Credentials{ Username: username, Password: password, TOTPSecret: totpSecret, } - c, err := hover.NewClient(creds, nil) + c, err := hoverclient.NewClient(creds, nil) if err != nil { return fmt.Errorf("hover: client init: %w", err) } diff --git a/internal/hover/client.go b/pkg/hoverclient/client.go similarity index 99% rename from internal/hover/client.go rename to pkg/hoverclient/client.go index 28984cd..2533505 100644 --- a/internal/hover/client.go +++ b/pkg/hoverclient/client.go @@ -1,4 +1,4 @@ -package hover +package hoverclient import ( "bytes" diff --git a/internal/hover/client_test.go b/pkg/hoverclient/client_test.go similarity index 99% rename from internal/hover/client_test.go rename to pkg/hoverclient/client_test.go index 9787cdd..7f36127 100644 --- a/internal/hover/client_test.go +++ b/pkg/hoverclient/client_test.go @@ -1,4 +1,4 @@ -package hover +package hoverclient import ( "context" diff --git a/internal/hover/totp.go b/pkg/hoverclient/totp.go similarity index 96% rename from internal/hover/totp.go rename to pkg/hoverclient/totp.go index 0eb2f90..bdbd961 100644 --- a/internal/hover/totp.go +++ b/pkg/hoverclient/totp.go @@ -1,4 +1,4 @@ -// Package hover implements the Hover DNS provider client. +// Package hoverclient implements the Hover DNS provider client. // // Hover ships no official API. This package mimics the browser-side // authentication flow exposed by Hover's signin UI: @@ -8,7 +8,7 @@ // 3. Subsequent requests carry the session cookie jar. // // TOTP codes are RFC 6238 (HMAC-SHA1, 30s window, 6 digits). -package hover +package hoverclient import ( "crypto/hmac" diff --git a/internal/hover/totp_test.go b/pkg/hoverclient/totp_test.go similarity index 99% rename from internal/hover/totp_test.go rename to pkg/hoverclient/totp_test.go index 4439b05..5b8157f 100644 --- a/internal/hover/totp_test.go +++ b/pkg/hoverclient/totp_test.go @@ -1,4 +1,4 @@ -package hover +package hoverclient import ( "strings"