[PHASE-7] Add more protocol tests

This commit is contained in:
Savely Krendelhoff 2025-08-23 13:08:45 +07:00
parent 8aa5b91f24
commit 65945d34c0
No known key found for this signature in database
GPG key ID: F70DFD34F40238DE
3 changed files with 334 additions and 10 deletions

View file

@ -0,0 +1,157 @@
package protocol
import (
"bytes"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"hash-of-wisdom/internal/pow/challenge"
"hash-of-wisdom/internal/quotes"
)
func TestSymmetricEncoding_ChallengeRequest(t *testing.T) {
// Create a challenge request
req := &ChallengeRequest{}
// Encode it
var buf bytes.Buffer
err := req.Encode(&buf)
require.NoError(t, err)
// Decode it back
decoder := NewMessageDecoder()
msg, err := decoder.Decode(&buf)
require.NoError(t, err)
assert.Equal(t, ChallengeRequestType, msg.Type)
assert.Equal(t, uint32(0), msg.PayloadLength)
// Decode the request payload
decodedReq := &ChallengeRequest{}
err = decodedReq.Decode(msg.PayloadStream)
require.NoError(t, err)
}
func TestSymmetricEncoding_SolutionRequest(t *testing.T) {
// Create a solution request
req := &SolutionRequest{
Challenge: challenge.Challenge{
Timestamp: 1640995200,
Difficulty: 4,
Resource: "quotes",
Random: []byte("test"),
HMAC: []byte("test"),
},
Nonce: 12345,
}
// Encode it
var buf bytes.Buffer
err := req.Encode(&buf)
require.NoError(t, err)
// Decode it back
decoder := NewMessageDecoder()
msg, err := decoder.Decode(&buf)
require.NoError(t, err)
assert.Equal(t, SolutionRequestType, msg.Type)
assert.Greater(t, msg.PayloadLength, uint32(0))
// Decode the request payload
decodedReq := &SolutionRequest{}
err = decodedReq.Decode(msg.PayloadStream)
require.NoError(t, err)
assert.Equal(t, req.Nonce, decodedReq.Nonce)
assert.Equal(t, req.Challenge.Timestamp, decodedReq.Challenge.Timestamp)
}
func TestSymmetricEncoding_ChallengeResponse(t *testing.T) {
// Create a challenge response
resp := &ChallengeResponse{
Challenge: &challenge.Challenge{
Timestamp: 1640995200,
Difficulty: 4,
Resource: "quotes",
Random: []byte("test"),
HMAC: []byte("test"),
},
}
// Encode it
var buf bytes.Buffer
err := resp.Encode(&buf)
require.NoError(t, err)
// Decode it back
decoder := NewMessageDecoder()
msg, err := decoder.Decode(&buf)
require.NoError(t, err)
assert.Equal(t, ChallengeResponseType, msg.Type)
assert.Greater(t, msg.PayloadLength, uint32(0))
// Decode the response payload
decodedResp := &ChallengeResponse{}
err = decodedResp.Decode(msg.PayloadStream)
require.NoError(t, err)
assert.Equal(t, resp.Challenge.Timestamp, decodedResp.Challenge.Timestamp)
assert.Equal(t, resp.Challenge.Difficulty, decodedResp.Challenge.Difficulty)
}
func TestSymmetricEncoding_SolutionResponse(t *testing.T) {
// Create a solution response
resp := &SolutionResponse{
Quote: &quotes.Quote{
Text: "Test quote",
Author: "Test Author",
},
}
// Encode it
var buf bytes.Buffer
err := resp.Encode(&buf)
require.NoError(t, err)
// Decode it back
decoder := NewMessageDecoder()
msg, err := decoder.Decode(&buf)
require.NoError(t, err)
assert.Equal(t, QuoteResponseType, msg.Type)
assert.Greater(t, msg.PayloadLength, uint32(0))
// Decode the response payload
decodedResp := &SolutionResponse{}
err = decodedResp.Decode(msg.PayloadStream)
require.NoError(t, err)
assert.Equal(t, resp.Quote.Text, decodedResp.Quote.Text)
assert.Equal(t, resp.Quote.Author, decodedResp.Quote.Author)
}
func TestSymmetricEncoding_ErrorResponse(t *testing.T) {
// Create an error response
resp := &ErrorResponse{
Code: "TEST_ERROR",
Message: "Test error message",
Details: map[string]string{"key": "value"},
}
// Encode it
var buf bytes.Buffer
err := resp.Encode(&buf)
require.NoError(t, err)
// Decode it back
decoder := NewMessageDecoder()
msg, err := decoder.Decode(&buf)
require.NoError(t, err)
assert.Equal(t, ErrorResponseType, msg.Type)
assert.Greater(t, msg.PayloadLength, uint32(0))
// Decode the response payload
decodedResp := &ErrorResponse{}
err = decodedResp.Decode(msg.PayloadStream)
require.NoError(t, err)
assert.Equal(t, resp.Code, decodedResp.Code)
assert.Equal(t, resp.Message, decodedResp.Message)
assert.Equal(t, resp.Details, decodedResp.Details)
}

View file

@ -113,16 +113,6 @@ func TestChallengeRequest_EmptyPayload(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
} }
func TestMessageDecoder_RejectsResponseTypes(t *testing.T) {
decoder := NewMessageDecoder()
data := []byte{byte(ErrorResponseType), 0x00, 0x00, 0x00, 0x05, 'h', 'e', 'l', 'l', 'o'}
buf := bytes.NewBuffer(data)
_, err := decoder.Decode(buf)
assert.Error(t, err)
assert.Contains(t, err.Error(), "invalid message type")
}
func TestPayloadStream_LimitedRead(t *testing.T) { func TestPayloadStream_LimitedRead(t *testing.T) {
decoder := NewMessageDecoder() decoder := NewMessageDecoder()

View file

@ -0,0 +1,177 @@
package protocol
import (
"bytes"
"encoding/json"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"hash-of-wisdom/internal/pow/challenge"
"hash-of-wisdom/internal/quotes"
)
// TestSpecCompliance_ChallengeResponse verifies challenge response matches PROTOCOL.md format
func TestSpecCompliance_ChallengeResponse(t *testing.T) {
resp := &ChallengeResponse{
Challenge: &challenge.Challenge{
Timestamp: 1640995200,
Difficulty: 4,
Resource: "quotes",
Random: []byte{0xa1, 0xb2, 0xc3, 0xd4, 0xe5, 0xf6},
HMAC: []byte("base64url_encoded_signature"),
},
}
var buf bytes.Buffer
err := resp.Encode(&buf)
require.NoError(t, err)
// Skip header (5 bytes) and get payload
header := buf.Bytes()[:5]
payload := buf.Bytes()[5:]
// Verify header format
assert.Equal(t, byte(ChallengeResponseType), header[0])
assert.Equal(t, uint32(len(payload)), uint32(header[1])<<24|uint32(header[2])<<16|uint32(header[3])<<8|uint32(header[4]))
// Verify JSON payload matches spec format
var decoded map[string]interface{}
err = json.Unmarshal(payload, &decoded)
require.NoError(t, err)
// Check required fields from spec
assert.Contains(t, decoded, "timestamp")
assert.Contains(t, decoded, "difficulty")
assert.Contains(t, decoded, "resource")
assert.Contains(t, decoded, "random")
assert.Contains(t, decoded, "hmac")
assert.Equal(t, float64(1640995200), decoded["timestamp"])
assert.Equal(t, float64(4), decoded["difficulty"])
assert.Equal(t, "quotes", decoded["resource"])
}
// TestSpecCompliance_SolutionRequest verifies solution request matches PROTOCOL.md format
func TestSpecCompliance_SolutionRequest(t *testing.T) {
req := &SolutionRequest{
Challenge: challenge.Challenge{
Timestamp: 1640995200,
Difficulty: 4,
Resource: "quotes",
Random: []byte{0xa1, 0xb2, 0xc3, 0xd4, 0xe5, 0xf6},
HMAC: []byte("base64url_encoded_signature"),
},
Nonce: 12345,
}
var buf bytes.Buffer
err := req.Encode(&buf)
require.NoError(t, err)
// Skip header and get payload
payload := buf.Bytes()[5:]
// Verify JSON payload matches spec format
var decoded map[string]interface{}
err = json.Unmarshal(payload, &decoded)
require.NoError(t, err)
// Check required top-level fields
assert.Contains(t, decoded, "challenge")
assert.Contains(t, decoded, "nonce")
assert.Equal(t, float64(12345), decoded["nonce"])
// Check challenge structure
challenge := decoded["challenge"].(map[string]interface{})
assert.Contains(t, challenge, "timestamp")
assert.Contains(t, challenge, "difficulty")
assert.Contains(t, challenge, "resource")
assert.Contains(t, challenge, "random")
assert.Contains(t, challenge, "hmac")
}
// TestSpecCompliance_QuoteResponse verifies quote response matches PROTOCOL.md format
func TestSpecCompliance_QuoteResponse(t *testing.T) {
resp := &SolutionResponse{
Quote: &quotes.Quote{
Text: "The only way to do great work is to love what you do.",
Author: "Steve Jobs",
},
}
var buf bytes.Buffer
err := resp.Encode(&buf)
require.NoError(t, err)
// Skip header and get payload
payload := buf.Bytes()[5:]
// Verify JSON payload matches spec format
var decoded map[string]interface{}
err = json.Unmarshal(payload, &decoded)
require.NoError(t, err)
// Check required fields from spec
assert.Contains(t, decoded, "text")
assert.Contains(t, decoded, "author")
assert.Equal(t, "The only way to do great work is to love what you do.", decoded["text"])
assert.Equal(t, "Steve Jobs", decoded["author"])
}
// TestSpecCompliance_ErrorResponse verifies error response matches PROTOCOL.md format
func TestSpecCompliance_ErrorResponse(t *testing.T) {
resp := &ErrorResponse{
Code: "INVALID_SOLUTION",
Message: "The provided PoW solution is incorrect",
RetryAfter: 30,
Details: map[string]string{"reason": "hash verification failed"},
}
var buf bytes.Buffer
err := resp.Encode(&buf)
require.NoError(t, err)
// Skip header and get payload
payload := buf.Bytes()[5:]
// Verify JSON payload matches spec format
var decoded map[string]interface{}
err = json.Unmarshal(payload, &decoded)
require.NoError(t, err)
// Check required fields from spec
assert.Contains(t, decoded, "code")
assert.Contains(t, decoded, "message")
assert.Equal(t, "INVALID_SOLUTION", decoded["code"])
assert.Equal(t, "The provided PoW solution is incorrect", decoded["message"])
// Check optional fields
assert.Contains(t, decoded, "retry_after")
assert.Contains(t, decoded, "details")
assert.Equal(t, float64(30), decoded["retry_after"])
}
// TestSpecCompliance_MessageSizeLimits verifies 8KB payload limit
func TestSpecCompliance_MessageSizeLimits(t *testing.T) {
decoder := NewMessageDecoder()
// Create a message that exceeds 8KB payload limit
largePayload := make([]byte, MaxPayloadSize+1)
for i := range largePayload {
largePayload[i] = 'A'
}
// Create message with oversized payload
var buf bytes.Buffer
buf.WriteByte(byte(ChallengeRequestType))
buf.Write([]byte{0x00, 0x00, 0x20, 0x01}) // 8193 bytes (8KB + 1)
buf.Write(largePayload)
// Should reject oversized payload
_, err := decoder.Decode(&buf)
assert.Error(t, err)
assert.Contains(t, err.Error(), "payload length")
assert.Contains(t, err.Error(), "exceeds maximum")
}