178 lines
5.2 KiB
Go
178 lines
5.2 KiB
Go
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: "es.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")
|
|
}
|