[PHASE-7] Add more protocol tests
This commit is contained in:
parent
8aa5b91f24
commit
65945d34c0
157
internal/protocol/encode_decode_test.go
Normal file
157
internal/protocol/encode_decode_test.go
Normal 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: "es.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)
|
||||
}
|
||||
|
|
@ -113,16 +113,6 @@ func TestChallengeRequest_EmptyPayload(t *testing.T) {
|
|||
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) {
|
||||
decoder := NewMessageDecoder()
|
||||
|
|
|
|||
177
internal/protocol/spec_compliance_test.go
Normal file
177
internal/protocol/spec_compliance_test.go
Normal 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: "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")
|
||||
}
|
||||
Loading…
Reference in a new issue