package cryptostrategy import ( "strings" "testing" "aiaa-notification-service/internal/engine" ) const sampleBody = `{ "eventType": "SIGNAL_RECEIVED", "correlationId": "0_0_0", "symbol": "QNT", "direction": "LONG", "payload": "{\"strategyCode\":\"ai-crypto-signals\",\"period\":\"1h\",\"currency\":\"QNT\",\"isSale\":false,\"isClose\":false,\"isGain\":false,\"gainTarget\":5,\"price\":58.23,\"lossPrice\":57.82,\"gainPrices\":\"58.435,58.64,58.845,59.05,59.255\",\"remark\":\"{\\\"orderId\\\":\\\"jeJY8l5YnYwfJbmj6zb4\\\"}\",\"totalGainTarget\":5,\"leverage\":43}", "eventTime": 1786802842899 }` func TestConvertParsesNestedPayload(t *testing.T) { event, data, err := Convert([]byte(sampleBody)) if err != nil { t.Fatal(err) } if event != "trade.open" { t.Fatalf("event=%q", event) } if data["symbol"] != "QNT" || data["strategyCode"] != "ai-crypto-signals" { t.Fatalf("data=%v", data) } if data["period"] != "1h" || data["direction"] != "LONG" { t.Fatalf("data=%v", data) } if data["orderId"] != "jeJY8l5YnYwfJbmj6zb4" { t.Fatalf("orderId=%v", data["orderId"]) } formatted, _ := data["formatted"].(string) for _, want := range []string{ "多单开仓", "交易品种: QNT", "周期: 1h", "开仓价格: 58.23", "止损价格: 57.82", "止盈价格: 58.435, 58.64, 58.845, 59.05, 59.255", "杠杆: 43x", "策略: ai-crypto-signals", } { if !strings.Contains(formatted, want) { t.Fatalf("missing %q in\n%s", want, formatted) } } } func TestConvertCloseFlag(t *testing.T) { body := []byte(`{ "eventType":"SIGNAL_RECEIVED","symbol":"BTC","direction":"SHORT", "payload":"{\"isClose\":true,\"price\":64000,\"strategyCode\":\"x\",\"period\":\"4h\"}", "eventTime":1786802842899 }`) event, data, err := Convert(body) if err != nil { t.Fatal(err) } if event != "trade.close" { t.Fatalf("event=%q", event) } formatted, _ := data["formatted"].(string) if !strings.Contains(formatted, "空单平仓") { t.Fatalf("%s", formatted) } } func TestConvertFlatOneLayer(t *testing.T) { body := []byte(`{ "eventType":"SIGNAL_RECEIVED", "correlationId":"0_0_0", "symbol":"QNT", "direction":"LONG", "strategyCode":"ai-crypto-signals", "period":"1h", "currency":"QNT", "isSale":false, "isClose":false, "isGain":false, "gainTarget":5, "price":58.23, "lossPrice":57.82, "gainPrices":"58.435,58.64", "remark":"{\"orderId\":\"jeJY8l5YnYwfJbmj6zb4\"}", "leverage":43, "eventTime":1786802842899 }`) event, data, err := Convert(body) if err != nil { t.Fatal(err) } if event != "trade.open" { t.Fatalf("event=%q", event) } if data["strategyCode"] != "ai-crypto-signals" || data["price"] != 58.23 { t.Fatalf("data=%v", data) } if data["orderId"] != "jeJY8l5YnYwfJbmj6zb4" { t.Fatalf("orderId=%v", data["orderId"]) } formatted, _ := data["formatted"].(string) if !strings.Contains(formatted, "多单开仓") || !strings.Contains(formatted, "开仓价格: 58.23") { t.Fatalf("%s", formatted) } } func TestConvertPayloadObject(t *testing.T) { body := []byte(`{ "eventType":"SIGNAL_RECEIVED","symbol":"ETH","direction":"SHORT", "payload":{"isClose":true,"price":3200,"strategyCode":"x","period":"1h"}, "eventTime":1786802842899 }`) event, data, err := Convert(body) if err != nil { t.Fatal(err) } if event != "trade.close" { t.Fatalf("event=%q", event) } if data["price"] != float64(3200) { t.Fatalf("data=%v", data) } } func TestConvertRendersSharedSignalTemplate(t *testing.T) { body := []byte(`{ "eventType": "SIGNAL_RECEIVED", "correlationId": "0_0_0", "symbol": "ICP", "direction": "LONG", "payload": "{\"strategyCode\":\"ai-crypto-signals\",\"period\":\"1h\",\"currency\":\"ICP\",\"isSale\":false,\"isClose\":true,\"isGain\":true,\"gainTarget\":1,\"price\":2.273,\"remark\":\"{\\\"orderId\\\":\\\"uJs3zQI8IaRV16n8NERq\\\",\\\"revenue\\\":\\\"14.1088%\\\"}\",\"totalGainTarget\":5}", "eventTime": 1786808929754 }`) _, data, err := Convert(body) if err != nil { t.Fatal(err) } for _, key := range []string{"totalAvgPx", "takeProfitPrice", "stopLossPrice", "side", "action", "symbol", "period", "price"} { if _, ok := data[key]; !ok { t.Fatalf("missing template key %q in %v", key, data) } } if data["orderId"] != "uJs3zQI8IaRV16n8NERq" || data["revenue"] != "14.1088%" { t.Fatalf("remark=%v", data) } tmpl := "### {{.symbol}} {{.action}}\n币种:{{.symbol}}\n周期:{{.period}}\n方向:{{.side}}\n价格:{{.price}}\n平均价:{{.totalAvgPx}}\n止盈价:{{.takeProfitPrice}}\n止损价:{{.stopLossPrice}}" out, err := engine.NewRenderer().Render(tmpl, data) if err != nil { t.Fatalf("render: %v", err) } if !strings.Contains(out, "ICP") || !strings.Contains(out, "CLOSE") { t.Fatalf("out=%s", out) } } func TestConvertInvalidJSON(t *testing.T) { _, _, err := Convert([]byte(`{not json`)) if err == nil { t.Fatal("expected error") } }