package application import ( "bytes" "context" "encoding/json" "errors" "testing" "hash-of-wisdom/internal/application/mocks" "hash-of-wisdom/internal/pow/challenge" "hash-of-wisdom/internal/protocol" "hash-of-wisdom/internal/quotes" "hash-of-wisdom/internal/service" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) func TestNewWisdomApplication(t *testing.T) { mockService := mocks.NewMockWisdomService(t) app := NewWisdomApplication(mockService) assert.NotNil(t, app) assert.Equal(t, mockService, app.wisdomService) } func TestWisdomApplication_HandleMessage_UnsupportedType(t *testing.T) { mockService := mocks.NewMockWisdomService(t) app := NewWisdomApplication(mockService) ctx := context.Background() msg := &protocol.Message{ Type: protocol.MessageType(0xFF), // Invalid type PayloadLength: 0, PayloadStream: nil, } response, err := app.HandleMessage(ctx, msg) require.NoError(t, err) // Type assert to ErrorResponse errorResponse, ok := response.(*protocol.ErrorResponse) require.True(t, ok, "Expected ErrorResponse") assert.Equal(t, protocol.ErrMalformedMessage, errorResponse.Code) assert.Contains(t, errorResponse.Message, "unsupported message type: 0xff") } func TestWisdomApplication_HandleChallengeRequest_Success(t *testing.T) { mockService := mocks.NewMockWisdomService(t) app := NewWisdomApplication(mockService) // Mock successful challenge generation testChallenge := &challenge.Challenge{ Resource: "quotes", Timestamp: 12345, Difficulty: 4, Random: []byte("test"), HMAC: []byte("signature"), } mockService.On("GenerateChallenge", mock.Anything, "quotes").Return(testChallenge, nil) ctx := context.Background() msg := &protocol.Message{ Type: protocol.ChallengeRequestType, PayloadLength: 0, PayloadStream: nil, } response, err := app.HandleMessage(ctx, msg) require.NoError(t, err) // Type assert to ChallengeResponse challengeResponse, ok := response.(*protocol.ChallengeResponse) require.True(t, ok, "Expected ChallengeResponse") assert.Equal(t, testChallenge, challengeResponse.Challenge) mockService.AssertExpectations(t) } func TestWisdomApplication_HandleChallengeRequest_ServiceError(t *testing.T) { mockService := mocks.NewMockWisdomService(t) app := NewWisdomApplication(mockService) // Mock service error mockService.On("GenerateChallenge", mock.Anything, "quotes").Return(nil, errors.New("service error")) ctx := context.Background() msg := &protocol.Message{ Type: protocol.ChallengeRequestType, PayloadLength: 0, PayloadStream: nil, } response, err := app.HandleMessage(ctx, msg) require.NoError(t, err) // Type assert to ErrorResponse errorResponse, ok := response.(*protocol.ErrorResponse) require.True(t, ok, "Expected ErrorResponse") assert.Equal(t, protocol.ErrServerError, errorResponse.Code) assert.Equal(t, "Contact administrator", errorResponse.Message) mockService.AssertExpectations(t) } func TestWisdomApplication_HandleSolutionRequest_Success(t *testing.T) { mockService := mocks.NewMockWisdomService(t) app := NewWisdomApplication(mockService) // Create test solution request testChallenge := challenge.Challenge{ Resource: "quotes", Timestamp: 12345, Difficulty: 4, Random: []byte("test"), HMAC: []byte("signature"), } solutionPayload := protocol.SolutionRequest{ Challenge: testChallenge, Nonce: 12345, } payloadJSON, err := json.Marshal(solutionPayload) require.NoError(t, err) testQuote := "es.Quote{ Text: "Test quote", Author: "Test Author", } // Mock successful verification and quote retrieval mockService.On("VerifySolution", mock.Anything, mock.AnythingOfType("*challenge.Solution")).Return(nil) mockService.On("GetQuote", mock.Anything).Return(testQuote, nil) ctx := context.Background() msg := &protocol.Message{ Type: protocol.SolutionRequestType, PayloadLength: uint32(len(payloadJSON)), PayloadStream: bytes.NewReader(payloadJSON), } response, err := app.HandleMessage(ctx, msg) require.NoError(t, err) // Type assert to SolutionResponse solutionResponse, ok := response.(*protocol.SolutionResponse) require.True(t, ok, "Expected SolutionResponse") assert.Equal(t, testQuote, solutionResponse.Quote) mockService.AssertExpectations(t) } func TestWisdomApplication_HandleSolutionRequest_InvalidJSON(t *testing.T) { mockService := mocks.NewMockWisdomService(t) app := NewWisdomApplication(mockService) invalidJSON := []byte("invalid json") ctx := context.Background() msg := &protocol.Message{ Type: protocol.SolutionRequestType, PayloadLength: uint32(len(invalidJSON)), PayloadStream: bytes.NewReader(invalidJSON), } response, err := app.HandleMessage(ctx, msg) require.NoError(t, err) // Type assert to ErrorResponse errorResponse, ok := response.(*protocol.ErrorResponse) require.True(t, ok, "Expected ErrorResponse") assert.Equal(t, protocol.ErrMalformedMessage, errorResponse.Code) assert.Equal(t, "invalid solution format", errorResponse.Message) mockService.AssertNotCalled(t, "VerifySolution") mockService.AssertNotCalled(t, "GetQuote") } func TestWisdomApplication_HandleSolutionRequest_VerificationFailed(t *testing.T) { mockService := mocks.NewMockWisdomService(t) app := NewWisdomApplication(mockService) // Create test solution request testChallenge := challenge.Challenge{ Resource: "quotes", Timestamp: 12345, Difficulty: 4, Random: []byte("test"), HMAC: []byte("signature"), } solutionPayload := protocol.SolutionRequest{ Challenge: testChallenge, Nonce: 12345, } payloadJSON, err := json.Marshal(solutionPayload) require.NoError(t, err) // Mock verification failure mockService.On("VerifySolution", mock.Anything, mock.AnythingOfType("*challenge.Solution")).Return(service.ErrInvalidSolution) ctx := context.Background() msg := &protocol.Message{ Type: protocol.SolutionRequestType, PayloadLength: uint32(len(payloadJSON)), PayloadStream: bytes.NewReader(payloadJSON), } response, err := app.HandleMessage(ctx, msg) require.NoError(t, err) // Type assert to ErrorResponse errorResponse, ok := response.(*protocol.ErrorResponse) require.True(t, ok, "Expected ErrorResponse") assert.Equal(t, protocol.ErrInvalidSolution, errorResponse.Code) assert.Equal(t, "solution verification failed", errorResponse.Message) mockService.AssertExpectations(t) mockService.AssertNotCalled(t, "GetQuote") } func TestWisdomApplication_HandleSolutionRequest_QuoteServiceError(t *testing.T) { mockService := mocks.NewMockWisdomService(t) app := NewWisdomApplication(mockService) // Create test solution request testChallenge := challenge.Challenge{ Resource: "quotes", Timestamp: 12345, Difficulty: 4, Random: []byte("test"), HMAC: []byte("signature"), } solutionPayload := protocol.SolutionRequest{ Challenge: testChallenge, Nonce: 12345, } payloadJSON, err := json.Marshal(solutionPayload) require.NoError(t, err) // Mock successful verification but quote service error mockService.On("VerifySolution", mock.Anything, mock.AnythingOfType("*challenge.Solution")).Return(nil) mockService.On("GetQuote", mock.Anything).Return(nil, errors.New("quote service error")) ctx := context.Background() msg := &protocol.Message{ Type: protocol.SolutionRequestType, PayloadLength: uint32(len(payloadJSON)), PayloadStream: bytes.NewReader(payloadJSON), } response, err := app.HandleMessage(ctx, msg) require.NoError(t, err) // Type assert to ErrorResponse errorResponse, ok := response.(*protocol.ErrorResponse) require.True(t, ok, "Expected ErrorResponse") assert.Equal(t, protocol.ErrServerError, errorResponse.Code) assert.Equal(t, "Contact administrator", errorResponse.Message) mockService.AssertExpectations(t) } func TestWisdomApplication_HandleMessage_ContextCancellation(t *testing.T) { mockService := mocks.NewMockWisdomService(t) app := NewWisdomApplication(mockService) // Create cancelled context ctx, cancel := context.WithCancel(context.Background()) cancel() // Mock service to respect context cancellation mockService.On("GenerateChallenge", mock.Anything, "quotes").Return(nil, context.Canceled) msg := &protocol.Message{ Type: protocol.ChallengeRequestType, PayloadLength: 0, PayloadStream: nil, } response, err := app.HandleMessage(ctx, msg) require.NoError(t, err) // Type assert to ErrorResponse errorResponse, ok := response.(*protocol.ErrorResponse) require.True(t, ok, "Expected ErrorResponse") assert.Equal(t, protocol.ErrServerError, errorResponse.Code) mockService.AssertExpectations(t) } func TestResponseEncoding(t *testing.T) { // Test ChallengeResponse encoding produces valid binary format testChallenge := &challenge.Challenge{ Resource: "quotes", Timestamp: 12345, Difficulty: 4, Random: []byte("test"), HMAC: []byte("signature"), } challengeResponse := &protocol.ChallengeResponse{Challenge: testChallenge} var buf bytes.Buffer err := challengeResponse.Encode(&buf) require.NoError(t, err) // Verify binary format data := buf.Bytes() assert.GreaterOrEqual(t, len(data), 5) // At least header size // Check message type assert.Equal(t, byte(protocol.ChallengeResponseType), data[0]) // Check payload contains expected data payload := string(data[5:]) // Skip header assert.Contains(t, payload, "quotes") assert.Contains(t, payload, "12345") }