- Implemented `useManualChallenge`, `useChallengePoll`, and `useManualChallengeMutations` hooks for managing manual DNS challenges. - Created tests for the `useManualChallenge` hooks to ensure correct fetching and mutation behavior. - Added `ManualDNSChallenge` component for displaying challenge details and actions. - Developed end-to-end tests for the Manual DNS Provider feature, covering provider selection, challenge UI, and accessibility compliance. - Included error handling tests for verification failures and network errors.
673 lines
19 KiB
Go
673 lines
19 KiB
Go
package services
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/Wikid82/charon/backend/internal/models"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"gorm.io/driver/sqlite"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// MockDNSResolver is a mock implementation of DNSResolver for testing.
|
|
type MockDNSResolver struct {
|
|
Records map[string][]string
|
|
LookupError error
|
|
}
|
|
|
|
func NewMockDNSResolver() *MockDNSResolver {
|
|
return &MockDNSResolver{
|
|
Records: make(map[string][]string),
|
|
}
|
|
}
|
|
|
|
func (m *MockDNSResolver) LookupTXT(ctx context.Context, name string) ([]string, error) {
|
|
if m.LookupError != nil {
|
|
return nil, m.LookupError
|
|
}
|
|
if records, ok := m.Records[name]; ok {
|
|
return records, nil
|
|
}
|
|
return nil, errors.New("no such host")
|
|
}
|
|
|
|
func (m *MockDNSResolver) SetRecords(fqdn string, values []string) {
|
|
m.Records[fqdn] = values
|
|
}
|
|
|
|
func setupManualChallengeTestDB(t *testing.T) *gorm.DB {
|
|
t.Helper()
|
|
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
|
|
require.NoError(t, err)
|
|
|
|
err = db.AutoMigrate(&models.ManualChallenge{})
|
|
require.NoError(t, err)
|
|
|
|
return db
|
|
}
|
|
|
|
func TestNewManualChallengeService(t *testing.T) {
|
|
db := setupManualChallengeTestDB(t)
|
|
service := NewManualChallengeService(db)
|
|
|
|
require.NotNil(t, service)
|
|
assert.NotNil(t, service.db)
|
|
assert.NotNil(t, service.cron)
|
|
assert.NotNil(t, service.resolver)
|
|
}
|
|
|
|
func TestManualChallengeService_SetResolver(t *testing.T) {
|
|
db := setupManualChallengeTestDB(t)
|
|
service := NewManualChallengeService(db)
|
|
|
|
mockResolver := NewMockDNSResolver()
|
|
service.SetResolver(mockResolver)
|
|
|
|
assert.Equal(t, mockResolver, service.resolver)
|
|
}
|
|
|
|
func TestManualChallengeService_CreateChallenge(t *testing.T) {
|
|
db := setupManualChallengeTestDB(t)
|
|
service := NewManualChallengeService(db)
|
|
|
|
ctx := context.Background()
|
|
req := CreateChallengeRequest{
|
|
ProviderID: 1,
|
|
UserID: 1,
|
|
FQDN: "_acme-challenge.example.com",
|
|
Token: "token123",
|
|
Value: "txtvalue456",
|
|
}
|
|
|
|
challenge, err := service.CreateChallenge(ctx, req)
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, challenge)
|
|
assert.NotEmpty(t, challenge.ID)
|
|
assert.Equal(t, uint(1), challenge.ProviderID)
|
|
assert.Equal(t, uint(1), challenge.UserID)
|
|
assert.Equal(t, "_acme-challenge.example.com", challenge.FQDN)
|
|
assert.Equal(t, "token123", challenge.Token)
|
|
assert.Equal(t, "txtvalue456", challenge.Value)
|
|
assert.Equal(t, models.ChallengeStatusPending, challenge.Status)
|
|
assert.False(t, challenge.DNSPropagated)
|
|
assert.True(t, challenge.ExpiresAt.After(time.Now()))
|
|
}
|
|
|
|
func TestManualChallengeService_CreateChallenge_SameUserReturnsExisting(t *testing.T) {
|
|
db := setupManualChallengeTestDB(t)
|
|
service := NewManualChallengeService(db)
|
|
|
|
ctx := context.Background()
|
|
req := CreateChallengeRequest{
|
|
ProviderID: 1,
|
|
UserID: 1,
|
|
FQDN: "_acme-challenge.example.com",
|
|
Token: "token123",
|
|
Value: "txtvalue456",
|
|
}
|
|
|
|
// Create first challenge
|
|
challenge1, err := service.CreateChallenge(ctx, req)
|
|
require.NoError(t, err)
|
|
|
|
// Try to create another for same FQDN and user
|
|
challenge2, err := service.CreateChallenge(ctx, req)
|
|
require.NoError(t, err)
|
|
|
|
// Should return the same challenge
|
|
assert.Equal(t, challenge1.ID, challenge2.ID)
|
|
}
|
|
|
|
func TestManualChallengeService_GetChallenge(t *testing.T) {
|
|
db := setupManualChallengeTestDB(t)
|
|
service := NewManualChallengeService(db)
|
|
|
|
ctx := context.Background()
|
|
req := CreateChallengeRequest{
|
|
ProviderID: 1,
|
|
UserID: 1,
|
|
FQDN: "_acme-challenge.example.com",
|
|
Token: "token123",
|
|
Value: "txtvalue456",
|
|
}
|
|
|
|
created, err := service.CreateChallenge(ctx, req)
|
|
require.NoError(t, err)
|
|
|
|
// Get the challenge
|
|
challenge, err := service.GetChallenge(ctx, created.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, created.ID, challenge.ID)
|
|
}
|
|
|
|
func TestManualChallengeService_GetChallenge_NotFound(t *testing.T) {
|
|
db := setupManualChallengeTestDB(t)
|
|
service := NewManualChallengeService(db)
|
|
|
|
ctx := context.Background()
|
|
_, err := service.GetChallenge(ctx, "nonexistent-id")
|
|
|
|
assert.ErrorIs(t, err, ErrChallengeNotFound)
|
|
}
|
|
|
|
func TestManualChallengeService_GetChallengeForUser(t *testing.T) {
|
|
db := setupManualChallengeTestDB(t)
|
|
service := NewManualChallengeService(db)
|
|
|
|
ctx := context.Background()
|
|
req := CreateChallengeRequest{
|
|
ProviderID: 1,
|
|
UserID: 1,
|
|
FQDN: "_acme-challenge.example.com",
|
|
Token: "token123",
|
|
Value: "txtvalue456",
|
|
}
|
|
|
|
created, err := service.CreateChallenge(ctx, req)
|
|
require.NoError(t, err)
|
|
|
|
// Same user should be able to access
|
|
challenge, err := service.GetChallengeForUser(ctx, created.ID, 1)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, created.ID, challenge.ID)
|
|
|
|
// Different user should get unauthorized error
|
|
_, err = service.GetChallengeForUser(ctx, created.ID, 2)
|
|
assert.ErrorIs(t, err, ErrUnauthorized)
|
|
}
|
|
|
|
func TestManualChallengeService_ListChallengesForProvider(t *testing.T) {
|
|
db := setupManualChallengeTestDB(t)
|
|
service := NewManualChallengeService(db)
|
|
|
|
ctx := context.Background()
|
|
|
|
// Create challenges for provider 1
|
|
for i := 0; i < 3; i++ {
|
|
_, err := service.CreateChallenge(ctx, CreateChallengeRequest{
|
|
ProviderID: 1,
|
|
UserID: 1,
|
|
FQDN: "_acme-challenge" + string(rune('a'+i)) + ".example.com",
|
|
Value: "value" + string(rune('a'+i)),
|
|
})
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// Create challenge for different provider
|
|
_, err := service.CreateChallenge(ctx, CreateChallengeRequest{
|
|
ProviderID: 2,
|
|
UserID: 1,
|
|
FQDN: "_acme-challenge.other.com",
|
|
Value: "other",
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// List challenges for provider 1
|
|
challenges, err := service.ListChallengesForProvider(ctx, 1, 1)
|
|
require.NoError(t, err)
|
|
assert.Len(t, challenges, 3)
|
|
|
|
// List challenges for provider 2
|
|
challenges, err = service.ListChallengesForProvider(ctx, 2, 1)
|
|
require.NoError(t, err)
|
|
assert.Len(t, challenges, 1)
|
|
}
|
|
|
|
func TestManualChallengeService_VerifyChallenge_DNSFound(t *testing.T) {
|
|
db := setupManualChallengeTestDB(t)
|
|
service := NewManualChallengeService(db)
|
|
|
|
mockResolver := NewMockDNSResolver()
|
|
service.SetResolver(mockResolver)
|
|
|
|
ctx := context.Background()
|
|
req := CreateChallengeRequest{
|
|
ProviderID: 1,
|
|
UserID: 1,
|
|
FQDN: "_acme-challenge.example.com",
|
|
Token: "token123",
|
|
Value: "txtvalue456",
|
|
}
|
|
|
|
challenge, err := service.CreateChallenge(ctx, req)
|
|
require.NoError(t, err)
|
|
|
|
// Set up mock DNS to return the expected value
|
|
mockResolver.SetRecords("_acme-challenge.example.com", []string{"txtvalue456"})
|
|
|
|
// Verify the challenge
|
|
result, err := service.VerifyChallenge(ctx, challenge.ID, 1)
|
|
require.NoError(t, err)
|
|
assert.True(t, result.Success)
|
|
assert.True(t, result.DNSFound)
|
|
assert.Equal(t, "verified", result.Status)
|
|
}
|
|
|
|
func TestManualChallengeService_VerifyChallenge_DNSNotFound(t *testing.T) {
|
|
db := setupManualChallengeTestDB(t)
|
|
service := NewManualChallengeService(db)
|
|
|
|
mockResolver := NewMockDNSResolver()
|
|
service.SetResolver(mockResolver)
|
|
|
|
ctx := context.Background()
|
|
req := CreateChallengeRequest{
|
|
ProviderID: 1,
|
|
UserID: 1,
|
|
FQDN: "_acme-challenge.example.com",
|
|
Token: "token123",
|
|
Value: "txtvalue456",
|
|
}
|
|
|
|
challenge, err := service.CreateChallenge(ctx, req)
|
|
require.NoError(t, err)
|
|
|
|
// DNS not set up - should not find record
|
|
result, err := service.VerifyChallenge(ctx, challenge.ID, 1)
|
|
require.NoError(t, err)
|
|
assert.False(t, result.Success)
|
|
assert.False(t, result.DNSFound)
|
|
assert.Equal(t, "pending", result.Status)
|
|
}
|
|
|
|
func TestManualChallengeService_VerifyChallenge_Expired(t *testing.T) {
|
|
db := setupManualChallengeTestDB(t)
|
|
service := NewManualChallengeService(db)
|
|
|
|
ctx := context.Background()
|
|
|
|
// Create an expired challenge directly
|
|
challenge := &models.ManualChallenge{
|
|
ID: "expired-challenge",
|
|
ProviderID: 1,
|
|
UserID: 1,
|
|
FQDN: "_acme-challenge.example.com",
|
|
Value: "value",
|
|
Status: models.ChallengeStatusPending,
|
|
CreatedAt: time.Now().Add(-20 * time.Minute),
|
|
ExpiresAt: time.Now().Add(-10 * time.Minute), // Already expired
|
|
}
|
|
err := db.Create(challenge).Error
|
|
require.NoError(t, err)
|
|
|
|
// Verify should fail with expired error
|
|
_, err = service.VerifyChallenge(ctx, challenge.ID, 1)
|
|
assert.ErrorIs(t, err, ErrChallengeExpired)
|
|
}
|
|
|
|
func TestManualChallengeService_VerifyChallenge_AlreadyVerified(t *testing.T) {
|
|
db := setupManualChallengeTestDB(t)
|
|
service := NewManualChallengeService(db)
|
|
|
|
ctx := context.Background()
|
|
|
|
// Create a verified challenge directly
|
|
now := time.Now()
|
|
challenge := &models.ManualChallenge{
|
|
ID: "verified-challenge",
|
|
ProviderID: 1,
|
|
UserID: 1,
|
|
FQDN: "_acme-challenge.example.com",
|
|
Value: "value",
|
|
Status: models.ChallengeStatusVerified,
|
|
DNSPropagated: true,
|
|
CreatedAt: now.Add(-5 * time.Minute),
|
|
ExpiresAt: now.Add(5 * time.Minute),
|
|
VerifiedAt: &now,
|
|
}
|
|
err := db.Create(challenge).Error
|
|
require.NoError(t, err)
|
|
|
|
// Verify should return success
|
|
result, err := service.VerifyChallenge(ctx, challenge.ID, 1)
|
|
require.NoError(t, err)
|
|
assert.True(t, result.Success)
|
|
assert.Equal(t, "verified", result.Status)
|
|
}
|
|
|
|
func TestManualChallengeService_PollChallengeStatus(t *testing.T) {
|
|
db := setupManualChallengeTestDB(t)
|
|
service := NewManualChallengeService(db)
|
|
|
|
ctx := context.Background()
|
|
req := CreateChallengeRequest{
|
|
ProviderID: 1,
|
|
UserID: 1,
|
|
FQDN: "_acme-challenge.example.com",
|
|
Value: "txtvalue456",
|
|
}
|
|
|
|
challenge, err := service.CreateChallenge(ctx, req)
|
|
require.NoError(t, err)
|
|
|
|
status, err := service.PollChallengeStatus(ctx, challenge.ID, 1)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, challenge.ID, status.ID)
|
|
assert.Equal(t, "pending", status.Status)
|
|
assert.False(t, status.DNSPropagated)
|
|
assert.Greater(t, status.TimeRemainingSeconds, 0)
|
|
}
|
|
|
|
func TestManualChallengeService_DeleteChallenge(t *testing.T) {
|
|
db := setupManualChallengeTestDB(t)
|
|
service := NewManualChallengeService(db)
|
|
|
|
ctx := context.Background()
|
|
req := CreateChallengeRequest{
|
|
ProviderID: 1,
|
|
UserID: 1,
|
|
FQDN: "_acme-challenge.example.com",
|
|
Value: "txtvalue456",
|
|
}
|
|
|
|
challenge, err := service.CreateChallenge(ctx, req)
|
|
require.NoError(t, err)
|
|
|
|
// Delete the challenge
|
|
err = service.DeleteChallenge(ctx, challenge.ID, 1)
|
|
require.NoError(t, err)
|
|
|
|
// Should not be found anymore
|
|
_, err = service.GetChallenge(ctx, challenge.ID)
|
|
assert.ErrorIs(t, err, ErrChallengeNotFound)
|
|
}
|
|
|
|
func TestManualChallengeService_DeleteChallenge_Unauthorized(t *testing.T) {
|
|
db := setupManualChallengeTestDB(t)
|
|
service := NewManualChallengeService(db)
|
|
|
|
ctx := context.Background()
|
|
req := CreateChallengeRequest{
|
|
ProviderID: 1,
|
|
UserID: 1,
|
|
FQDN: "_acme-challenge.example.com",
|
|
Value: "txtvalue456",
|
|
}
|
|
|
|
challenge, err := service.CreateChallenge(ctx, req)
|
|
require.NoError(t, err)
|
|
|
|
// Try to delete as different user
|
|
err = service.DeleteChallenge(ctx, challenge.ID, 2)
|
|
assert.ErrorIs(t, err, ErrUnauthorized)
|
|
}
|
|
|
|
func TestManualChallengeService_GetActiveChallengeForFQDN(t *testing.T) {
|
|
db := setupManualChallengeTestDB(t)
|
|
service := NewManualChallengeService(db)
|
|
|
|
ctx := context.Background()
|
|
fqdn := "_acme-challenge.example.com"
|
|
|
|
// No active challenge yet
|
|
challenge, err := service.GetActiveChallengeForFQDN(ctx, fqdn, 1)
|
|
require.NoError(t, err)
|
|
assert.Nil(t, challenge)
|
|
|
|
// Create a challenge
|
|
req := CreateChallengeRequest{
|
|
ProviderID: 1,
|
|
UserID: 1,
|
|
FQDN: fqdn,
|
|
Value: "txtvalue456",
|
|
}
|
|
created, err := service.CreateChallenge(ctx, req)
|
|
require.NoError(t, err)
|
|
|
|
// Now should find active challenge
|
|
challenge, err = service.GetActiveChallengeForFQDN(ctx, fqdn, 1)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, challenge)
|
|
assert.Equal(t, created.ID, challenge.ID)
|
|
}
|
|
|
|
func TestManualChallengeService_checkDNSPropagation(t *testing.T) {
|
|
db := setupManualChallengeTestDB(t)
|
|
service := NewManualChallengeService(db)
|
|
|
|
mockResolver := NewMockDNSResolver()
|
|
service.SetResolver(mockResolver)
|
|
|
|
ctx := context.Background()
|
|
fqdn := "_acme-challenge.example.com"
|
|
expectedValue := "txtvalue456"
|
|
|
|
// No record - should return false
|
|
found := service.checkDNSPropagation(ctx, fqdn, expectedValue)
|
|
assert.False(t, found)
|
|
|
|
// Add wrong value - should return false
|
|
mockResolver.SetRecords(fqdn, []string{"wrongvalue"})
|
|
found = service.checkDNSPropagation(ctx, fqdn, expectedValue)
|
|
assert.False(t, found)
|
|
|
|
// Add correct value - should return true
|
|
mockResolver.SetRecords(fqdn, []string{expectedValue})
|
|
found = service.checkDNSPropagation(ctx, fqdn, expectedValue)
|
|
assert.True(t, found)
|
|
|
|
// Multiple records including correct value - should return true
|
|
mockResolver.SetRecords(fqdn, []string{"other", expectedValue, "another"})
|
|
found = service.checkDNSPropagation(ctx, fqdn, expectedValue)
|
|
assert.True(t, found)
|
|
}
|
|
|
|
func TestManualChallengeService_checkDNSPropagation_Error(t *testing.T) {
|
|
db := setupManualChallengeTestDB(t)
|
|
service := NewManualChallengeService(db)
|
|
|
|
mockResolver := NewMockDNSResolver()
|
|
mockResolver.LookupError = errors.New("DNS lookup failed")
|
|
service.SetResolver(mockResolver)
|
|
|
|
ctx := context.Background()
|
|
|
|
found := service.checkDNSPropagation(ctx, "_acme-challenge.example.com", "value")
|
|
assert.False(t, found)
|
|
}
|
|
|
|
func TestManualChallengeService_StartStop(t *testing.T) {
|
|
db := setupManualChallengeTestDB(t)
|
|
service := NewManualChallengeService(db)
|
|
|
|
// Start should not panic
|
|
service.Start()
|
|
|
|
// Stop should not panic
|
|
service.Stop()
|
|
}
|
|
|
|
func TestDefaultDNSResolver_LookupTXT(t *testing.T) {
|
|
resolver := NewDefaultDNSResolver()
|
|
require.NotNil(t, resolver)
|
|
require.NotNil(t, resolver.resolver)
|
|
|
|
// This test may fail depending on network, so we just verify the resolver is created correctly
|
|
ctx := context.Background()
|
|
|
|
// Try to lookup a known domain (may fail in CI due to network)
|
|
_, err := resolver.LookupTXT(ctx, "google.com")
|
|
// We don't assert on the result since it depends on network
|
|
_ = err
|
|
}
|
|
|
|
func TestChallengeStatusResponse_Fields(t *testing.T) {
|
|
now := time.Now()
|
|
resp := &ChallengeStatusResponse{
|
|
ID: "test-id",
|
|
Status: "pending",
|
|
DNSPropagated: false,
|
|
TimeRemainingSeconds: 300,
|
|
LastCheckAt: &now,
|
|
}
|
|
|
|
assert.Equal(t, "test-id", resp.ID)
|
|
assert.Equal(t, "pending", resp.Status)
|
|
assert.False(t, resp.DNSPropagated)
|
|
assert.Equal(t, 300, resp.TimeRemainingSeconds)
|
|
assert.NotNil(t, resp.LastCheckAt)
|
|
}
|
|
|
|
func TestVerifyResult_Fields(t *testing.T) {
|
|
result := &VerifyResult{
|
|
Success: true,
|
|
DNSFound: true,
|
|
Message: "DNS TXT record verified successfully",
|
|
Status: "verified",
|
|
TimeRemaining: 0,
|
|
}
|
|
|
|
assert.True(t, result.Success)
|
|
assert.True(t, result.DNSFound)
|
|
assert.Equal(t, "DNS TXT record verified successfully", result.Message)
|
|
assert.Equal(t, "verified", result.Status)
|
|
}
|
|
|
|
func TestCreateChallengeRequest_Fields(t *testing.T) {
|
|
req := CreateChallengeRequest{
|
|
ProviderID: 1,
|
|
UserID: 2,
|
|
FQDN: "_acme-challenge.example.com",
|
|
Token: "token",
|
|
Value: "value",
|
|
}
|
|
|
|
assert.Equal(t, uint(1), req.ProviderID)
|
|
assert.Equal(t, uint(2), req.UserID)
|
|
assert.Equal(t, "_acme-challenge.example.com", req.FQDN)
|
|
assert.Equal(t, "token", req.Token)
|
|
assert.Equal(t, "value", req.Value)
|
|
}
|
|
|
|
func TestManualChallengeService_CleanupExpiredChallenges(t *testing.T) {
|
|
db := setupManualChallengeTestDB(t)
|
|
service := NewManualChallengeService(db)
|
|
|
|
// Create challenges with different states and expiration times
|
|
now := time.Now()
|
|
|
|
// Create an expired pending challenge
|
|
expiredChallenge := &models.ManualChallenge{
|
|
ID: "expired-pending",
|
|
ProviderID: 1,
|
|
UserID: 1,
|
|
FQDN: "_acme-challenge.expired.com",
|
|
Value: "value1",
|
|
Status: models.ChallengeStatusPending,
|
|
CreatedAt: now.Add(-20 * time.Minute),
|
|
ExpiresAt: now.Add(-10 * time.Minute),
|
|
}
|
|
require.NoError(t, db.Create(expiredChallenge).Error)
|
|
|
|
// Create an old challenge (should be deleted)
|
|
oldChallenge := &models.ManualChallenge{
|
|
ID: "old-challenge",
|
|
ProviderID: 1,
|
|
UserID: 1,
|
|
FQDN: "_acme-challenge.old.com",
|
|
Value: "value2",
|
|
Status: models.ChallengeStatusVerified,
|
|
CreatedAt: now.Add(-8 * 24 * time.Hour), // 8 days old
|
|
ExpiresAt: now.Add(-8 * 24 * time.Hour),
|
|
}
|
|
require.NoError(t, db.Create(oldChallenge).Error)
|
|
|
|
// Create an active challenge (should not be affected)
|
|
activeChallenge := &models.ManualChallenge{
|
|
ID: "active-challenge",
|
|
ProviderID: 1,
|
|
UserID: 1,
|
|
FQDN: "_acme-challenge.active.com",
|
|
Value: "value3",
|
|
Status: models.ChallengeStatusPending,
|
|
CreatedAt: now,
|
|
ExpiresAt: now.Add(10 * time.Minute),
|
|
}
|
|
require.NoError(t, db.Create(activeChallenge).Error)
|
|
|
|
// Run cleanup
|
|
service.cleanupExpiredChallenges()
|
|
|
|
// Verify expired challenge is marked as expired
|
|
var expiredResult models.ManualChallenge
|
|
db.Where("id = ?", "expired-pending").First(&expiredResult)
|
|
assert.Equal(t, models.ChallengeStatusExpired, expiredResult.Status)
|
|
|
|
// Verify old challenge is deleted
|
|
var oldCount int64
|
|
db.Model(&models.ManualChallenge{}).Where("id = ?", "old-challenge").Count(&oldCount)
|
|
assert.Equal(t, int64(0), oldCount)
|
|
|
|
// Verify active challenge is unchanged
|
|
var activeResult models.ManualChallenge
|
|
db.Where("id = ?", "active-challenge").First(&activeResult)
|
|
assert.Equal(t, models.ChallengeStatusPending, activeResult.Status)
|
|
}
|
|
|
|
func TestManualChallengeService_PollChallengeStatus_Expired(t *testing.T) {
|
|
db := setupManualChallengeTestDB(t)
|
|
service := NewManualChallengeService(db)
|
|
|
|
ctx := context.Background()
|
|
|
|
// Create an expired but not yet marked challenge
|
|
now := time.Now()
|
|
challenge := &models.ManualChallenge{
|
|
ID: "poll-expired",
|
|
ProviderID: 1,
|
|
UserID: 1,
|
|
FQDN: "_acme-challenge.expired.com",
|
|
Value: "value",
|
|
Status: models.ChallengeStatusPending,
|
|
CreatedAt: now.Add(-20 * time.Minute),
|
|
ExpiresAt: now.Add(-5 * time.Minute), // Already expired
|
|
}
|
|
require.NoError(t, db.Create(challenge).Error)
|
|
|
|
// Poll should mark it as expired
|
|
status, err := service.PollChallengeStatus(ctx, challenge.ID, 1)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "expired", status.Status)
|
|
}
|
|
|
|
func TestManualChallengeService_CreateChallenge_DifferentUser(t *testing.T) {
|
|
db := setupManualChallengeTestDB(t)
|
|
service := NewManualChallengeService(db)
|
|
|
|
ctx := context.Background()
|
|
|
|
// Create a challenge for user 1
|
|
_, err := service.CreateChallenge(ctx, CreateChallengeRequest{
|
|
ProviderID: 1,
|
|
UserID: 1,
|
|
FQDN: "_acme-challenge.example.com",
|
|
Value: "value1",
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Try to create another for same FQDN but different user
|
|
_, err = service.CreateChallenge(ctx, CreateChallengeRequest{
|
|
ProviderID: 1,
|
|
UserID: 2,
|
|
FQDN: "_acme-challenge.example.com",
|
|
Value: "value2",
|
|
})
|
|
assert.ErrorIs(t, err, ErrChallengeInProgress)
|
|
}
|
|
|
|
func TestManualChallengeService_ListChallengesForProvider_Empty(t *testing.T) {
|
|
db := setupManualChallengeTestDB(t)
|
|
service := NewManualChallengeService(db)
|
|
|
|
ctx := context.Background()
|
|
|
|
challenges, err := service.ListChallengesForProvider(ctx, 999, 1)
|
|
require.NoError(t, err)
|
|
assert.Empty(t, challenges)
|
|
}
|