package service import ( "context" "testing" "time" "hash-of-wisdom/internal/pow/challenge" "hash-of-wisdom/internal/quotes" "hash-of-wisdom/internal/service/mocks" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) func TestWisdomService_GenerateChallenge(t *testing.T) { tests := []struct { name string resource string wantErr error }{ { name: "valid resource", resource: "quotes", wantErr: nil, }, { name: "empty resource", resource: "", wantErr: ErrResourceRequired, }, { name: "unsupported resource", resource: "invalid", wantErr: ErrUnsupportedResource, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { mockGen := mocks.NewMockChallengeGenerator(t) mockVer := mocks.NewMockChallengeVerifier(t) mockQuote := mocks.NewMockQuoteService(t) service := NewWisdomService(mockGen, mockVer, mockQuote) if tt.wantErr == nil { expectedChallenge := &challenge.Challenge{ Timestamp: time.Now().Unix(), Difficulty: 4, Resource: "quotes", Random: []byte{0x01, 0x02, 0x03}, } mockGen.EXPECT().GenerateChallenge().Return(expectedChallenge, nil).Once() } ctx := context.Background() ch, err := service.GenerateChallenge(ctx, tt.resource) if tt.wantErr != nil { assert.ErrorIs(t, err, tt.wantErr) assert.Nil(t, ch) } else { assert.NoError(t, err) assert.NotNil(t, ch) } }) } } func TestWisdomService_VerifySolution(t *testing.T) { tests := []struct { name string solution *challenge.Solution wantErr error setupMocks func(*mocks.MockChallengeVerifier) }{ { name: "nil solution", solution: nil, wantErr: ErrSolutionRequired, setupMocks: func(mv *mocks.MockChallengeVerifier) {}, }, { name: "invalid challenge", solution: &challenge.Solution{ Challenge: challenge.Challenge{}, Nonce: 123, }, wantErr: ErrInvalidChallenge, setupMocks: func(mv *mocks.MockChallengeVerifier) { mv.EXPECT().VerifyChallenge(mock.Anything).Return(challenge.ErrInvalidHMAC).Once() }, }, { name: "invalid solution", solution: createInvalidSolution(t), wantErr: ErrInvalidSolution, setupMocks: func(mv *mocks.MockChallengeVerifier) { mv.EXPECT().VerifyChallenge(mock.Anything).Return(nil).Once() }, }, { name: "valid solution", solution: createValidSolution(t), wantErr: nil, setupMocks: func(mv *mocks.MockChallengeVerifier) { mv.EXPECT().VerifyChallenge(mock.Anything).Return(nil).Once() }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { mockGen := mocks.NewMockChallengeGenerator(t) mockVer := mocks.NewMockChallengeVerifier(t) mockQuote := mocks.NewMockQuoteService(t) tt.setupMocks(mockVer) service := NewWisdomService(mockGen, mockVer, mockQuote) ctx := context.Background() err := service.VerifySolution(ctx, tt.solution) if tt.wantErr != nil { assert.ErrorIs(t, err, tt.wantErr) } else { assert.NoError(t, err) } }) } } func TestWisdomService_GetQuote(t *testing.T) { mockGen := mocks.NewMockChallengeGenerator(t) mockVer := mocks.NewMockChallengeVerifier(t) mockQuote := mocks.NewMockQuoteService(t) expectedQuote := "es.Quote{ Text: "Test quote", Author: "Test author", } mockQuote.EXPECT().GetRandomQuote(mock.Anything).Return(expectedQuote, nil).Once() service := NewWisdomService(mockGen, mockVer, mockQuote) ctx := context.Background() quote, err := service.GetQuote(ctx) assert.NoError(t, err) assert.Equal(t, expectedQuote, quote) } func createValidSolution(t *testing.T) *challenge.Solution { config := challenge.TestConfig() ch := &challenge.Challenge{ Timestamp: time.Now().Unix(), Difficulty: 0, // Difficulty 0 means any nonce works Resource: "quotes", Random: []byte{0x01, 0x02, 0x03}, } ch.Sign(config.HMACSecret) return &challenge.Solution{ Challenge: *ch, Nonce: 0, } } func createInvalidSolution(t *testing.T) *challenge.Solution { config := challenge.TestConfig() ch := &challenge.Challenge{ Timestamp: time.Now().Unix(), Difficulty: 20, // High difficulty, nonce 999 won't work Resource: "quotes", Random: []byte{0x01, 0x02, 0x03}, } ch.Sign(config.HMACSecret) return &challenge.Solution{ Challenge: *ch, Nonce: 999, // Wrong nonce for difficulty 20 } }