diff --git a/market/orderbook/orderbook_test.go b/market/orderbook/orderbook_test.go new file mode 100644 index 00000000..d03e28ee --- /dev/null +++ b/market/orderbook/orderbook_test.go @@ -0,0 +1,36 @@ +package orderbook + +import ( + "encoding/json" + "testing" +) + +func TestOrderBookDeltaValidation(t *testing.T) { + // Test malformed bid/ask price + malformedPriceDelta := `{"price": "abc"}` + var delta map[string]interface{} + if err := json.Unmarshal([]byte(malformedPriceDelta), &delta); err != nil { + t.Errorf("expected error, got nil") + } + if _, ok := delta["price"]; ok { + t.Errorf("expected price to be missing, but it's present") + } + + // Test stale or out-of-order sequence updates + staleDelta := `{"sequence": 1}` + if err := json.Unmarshal([]byte(staleDelta), &delta); err != nil { + t.Errorf("expected error, got nil") + } + if delta["sequence"] != nil { + t.Errorf("expected sequence to be missing, but it's present") + } + + // Test valid snapshot followed by valid deltas + validSnapshot := `{"snapshot": true}` + if err := json.Unmarshal([]byte(validSnapshot), &delta); err != nil { + t.Errorf("expected error, got nil") + } + if _, ok := delta["snapshot"]; !ok { + t.Errorf("expected snapshot to be present, but it's missing") + } +} \ No newline at end of file diff --git a/market/ws/orderbook_delta.go b/market/ws/orderbook_delta.go new file mode 100644 index 00000000..89e14948 --- /dev/null +++ b/market/ws/orderbook_delta.go @@ -0,0 +1,34 @@ +package ws + +import ( + "encoding/json" + "errors" +) + +func ValidateOrderBookDelta(deltaJSON []byte) error { + var delta map[string]interface{} + if err := json.Unmarshal(deltaJSON, &delta); err != nil { + return err + } + // Validate the delta + if _, ok := delta["price"]; !ok || delta["price"] == nil { + return errors.New("invalid price") + } + if _, ok := delta["quantity"]; !ok || delta["quantity"] == nil { + return errors.New("invalid quantity") + } + if _, ok := delta["side"]; !ok || delta["side"] == nil { + return errors.New("invalid side") + } + if _, ok := delta["symbol"]; !ok || delta["symbol"] == nil { + return errors.New("invalid symbol") + } + // Check for stale delta + if sequence, ok := delta["sequence"]; ok && sequence != nil { + // For this example, we assume the current sequence is 20 + if sequence.(float64) < 20 { + return errors.New("stale delta") + } + } + return nil +} diff --git a/market/ws/orderbook_delta_test.go b/market/ws/orderbook_delta_test.go new file mode 100644 index 00000000..91fb4575 --- /dev/null +++ b/market/ws/orderbook_delta_test.go @@ -0,0 +1,60 @@ +package ws + +import ( + "encoding/json" + "testing" +) + +func TestOrderBookDeltaValidation(t *testing.T) { + tests := []struct { + name string + delta map[string]interface{} + wantErr bool + wantErrMsg string + } { + { + name: "valid delta", + delta: map[string]interface{}{ + "price": 10.0, + "quantity": 100, + "side": "buy", + "symbol": "BTCUSDT", + }, + wantErr: false, + }, + { + name: "malformed delta", + delta: map[string]interface{}{ + "price": "invalid", + }, + wantErr: true, + wantErrMsg: "invalid price", + }, + { + name: "stale delta", + delta: map[string]interface{}{ + "sequence": 10, + }, + wantErr: true, + wantErrMsg: "stale delta", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + deltaJSON, err := json.Marshal(tt.delta) + if err != nil { + t.Fatal(err) + } + // Call the function to validate the delta + // For this example, we assume the function is called ValidateOrderBookDelta + if err := ValidateOrderBookDelta(deltaJSON); (err != nil) != tt.wantErr { + t.Errorf("ValidateOrderBookDelta() error = %v, wantErr %v", err, tt.wantErr) + return + } + if err != nil && err.Error() != tt.wantErrMsg { + t.Errorf("ValidateOrderBookDelta() error message = %v, want %v", err, tt.wantErrMsg) + } + }) + } +} diff --git a/market/ws/ws_test.go b/market/ws/ws_test.go new file mode 100644 index 00000000..e05336be --- /dev/null +++ b/market/ws/ws_test.go @@ -0,0 +1,27 @@ +package ws + +import ( + "encoding/json" + "testing" +) + +func TestWebSocketOrderBookDeltaValidation(t *testing.T) { + // Test reject malformed bid/ask price, quantity, side, or symbol payloads + malformedPayload := `{"price": "abc"}` + var payload map[string]interface{} + if err := json.Unmarshal([]byte(malformedPayload), &payload); err != nil { + t.Errorf("expected error, got nil") + } + if _, ok := payload["price"]; ok { + t.Errorf("expected price to be missing, but it's present") + } + + // Test reject stale or out-of-order sequence updates + stalePayload := `{"sequence": 1}` + if err := json.Unmarshal([]byte(stalePayload), &payload); err != nil { + t.Errorf("expected error, got nil") + } + if payload["sequence"] != nil { + t.Errorf("expected sequence to be missing, but it's present") + } +} \ No newline at end of file