Files
indexer/zk/adapter_coverage_test.go
zeekayandHanzo Dev 64519916bf the corporate name is Lux Industries Inc
Lux Partners Limited is not the entity behind this work. Every place the
old name stood — copyright lines, site footers, brand config, legal and
compliance text — now names the company that actually holds it.

lux.fund is the exception and is not renamed to match: that site belongs
to Lux Assets Management LLC, a different company, and it says so.

Two kinds of line were left for a person rather than rewritten. A dated
news item recording that Lux Partners Limited was established in the Isle
of Man is a historical fact, and renaming it would falsify the record. And
a sentence asserting that the company is domiciled in the Isle of Man is a
claim about a jurisdiction, not a name — an Inc or an LLC is not an Isle
of Man company, so swapping the name there would assert something untrue.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-28 13:01:04 -07:00

324 lines
8.4 KiB
Go

// Copyright (c) 2025 Lux Industries Inc
// SPDX-License-Identifier: MIT
package zk
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
"github.com/luxfi/indexer/dag"
)
// TestParseVertexInvalidTransfers tests vertex with malformed transfers
func TestParseVertexInvalidTransfers(t *testing.T) {
adapter := New("")
vertexJSON := `{
"id": "zv-bad-tx",
"parentIds": [],
"height": 1,
"epoch": 1,
"timestamp": 1705323000,
"status": "pending",
"merkleRoot": "` + strings.Repeat("a", 64) + `",
"nullifierRoot": "` + strings.Repeat("b", 64) + `",
"transfers": "not-an-array"
}`
_, err := adapter.ParseVertex(json.RawMessage(vertexJSON))
if err == nil {
t.Error("expected error for invalid transfers field")
}
}
// TestParseVertexNoTransfersField tests vertex without transfers key
func TestParseVertexNoTransfersField(t *testing.T) {
adapter := New("")
vertexJSON := `{
"id": "zv-no-tx",
"parentIds": [],
"height": 1,
"epoch": 1,
"timestamp": 1705323000,
"status": "accepted",
"merkleRoot": "` + strings.Repeat("a", 64) + `",
"nullifierRoot": "` + strings.Repeat("b", 64) + `"
}`
v, err := adapter.ParseVertex(json.RawMessage(vertexJSON))
if err != nil {
t.Fatalf("ParseVertex failed: %v", err)
}
if len(v.TxIDs) != 0 {
t.Errorf("expected 0 txIDs, got %d", len(v.TxIDs))
}
}
// TestParseVertexMultipleTransfers tests extracting multiple tx IDs
func TestParseVertexMultipleTransfers(t *testing.T) {
adapter := New("")
vertexJSON := `{
"id": "zv-multi",
"parentIds": [],
"height": 100,
"epoch": 5,
"timestamp": 1705323000,
"status": "accepted",
"merkleRoot": "` + strings.Repeat("a", 64) + `",
"nullifierRoot": "` + strings.Repeat("b", 64) + `",
"proposer": "lux1prop",
"transfers": [
{"txId": "tx-1", "type": "shielded_transfer", "fee": 100},
{"txId": "tx-2", "type": "shield", "fee": 200},
{"txId": "tx-3", "type": "unshield", "fee": 300}
]
}`
v, err := adapter.ParseVertex(json.RawMessage(vertexJSON))
if err != nil {
t.Fatalf("ParseVertex failed: %v", err)
}
if len(v.TxIDs) != 3 {
t.Errorf("expected 3 txIDs, got %d", len(v.TxIDs))
}
if v.TxIDs[0] != "tx-1" {
t.Errorf("first txID: got %s, want tx-1", v.TxIDs[0])
}
if v.Metadata["transferCount"] != 3 {
t.Errorf("transferCount: got %v, want 3", v.Metadata["transferCount"])
}
}
// TestParseVertexAllStatusValues tests all status mapping paths
func TestParseVertexAllStatusValues(t *testing.T) {
adapter := New("")
tests := []struct {
input string
expect dag.Status
}{
{"accepted", dag.StatusAccepted},
{"Accepted", dag.StatusAccepted},
{"rejected", dag.StatusRejected},
{"Rejected", dag.StatusRejected},
{"pending", dag.StatusPending},
{"Processing", dag.StatusPending},
{"", dag.StatusPending},
}
for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
vertexJSON := `{
"id": "zv-s",
"parentIds": [],
"height": 1,
"epoch": 1,
"timestamp": 1705323000,
"status": "` + tt.input + `",
"merkleRoot": "` + strings.Repeat("a", 64) + `",
"nullifierRoot": "` + strings.Repeat("b", 64) + `"
}`
v, err := adapter.ParseVertex(json.RawMessage(vertexJSON))
if err != nil {
t.Fatalf("ParseVertex failed: %v", err)
}
if v.Status != tt.expect {
t.Errorf("got %s, want %s", v.Status, tt.expect)
}
})
}
}
// TestValidateNullifierHashEdgeCases tests more nullifier hash validations
func TestValidateNullifierHashEdgeCases(t *testing.T) {
tests := []struct {
name string
hash string
wantErr bool
}{
{"valid lowercase", strings.Repeat("ab", 32), false},
{"valid uppercase", strings.Repeat("AB", 32), false},
{"valid digits", strings.Repeat("01", 32), false},
{"spaces", strings.Repeat(" ", 64), true},
{"special chars", strings.Repeat("@!", 32), true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := ValidateNullifierHash(tt.hash)
if (err != nil) != tt.wantErr {
t.Errorf("error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
// TestValidateCommitmentHashEdgeCases tests more commitment hash validations
func TestValidateCommitmentHashEdgeCases(t *testing.T) {
tests := []struct {
name string
hash string
wantErr bool
}{
{"valid mixed", "aAbBcCdDeEfF00112233445566778899aabbccddeeff00112233445566778899", false},
{"63 chars", strings.Repeat("a", 63), true},
{"65 chars", strings.Repeat("a", 65), true},
{"valid 64 hex", strings.Repeat("0f", 32), false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := ValidateCommitmentHash(tt.hash)
if (err != nil) != tt.wantErr {
t.Errorf("error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
// TestGetNullifierRPCError tests RPC error from GetNullifier
func TestGetNullifierRPCError(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
response := RPCResponse{
JSONRPC: "2.0",
ID: 1,
Error: &RPCError{Code: -32000, Message: "nullifier not found"},
}
json.NewEncoder(w).Encode(response)
}))
defer server.Close()
adapter := New(server.URL)
_, err := adapter.GetNullifier(context.Background(), strings.Repeat("a", 64))
if err == nil {
t.Error("expected error for RPC error response")
}
}
// TestGetCommitmentRPCError tests RPC error from GetCommitment
func TestGetCommitmentRPCError(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
response := RPCResponse{
JSONRPC: "2.0",
ID: 1,
Error: &RPCError{Code: -32000, Message: "commitment not found"},
}
json.NewEncoder(w).Encode(response)
}))
defer server.Close()
adapter := New(server.URL)
_, err := adapter.GetCommitment(context.Background(), strings.Repeat("b", 64))
if err == nil {
t.Error("expected error for RPC error response")
}
}
// TestGetMerkleRootRPCError tests RPC error from GetMerkleRoot
func TestGetMerkleRootRPCError(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
response := RPCResponse{
JSONRPC: "2.0",
ID: 1,
Error: &RPCError{Code: -32000, Message: "chain unavailable"},
}
json.NewEncoder(w).Encode(response)
}))
defer server.Close()
adapter := New(server.URL)
_, err := adapter.GetMerkleRoot(context.Background())
if err == nil {
t.Error("expected error for RPC error response")
}
}
// TestVerifyProofRPCError tests RPC error from VerifyProof
func TestVerifyProofRPCError(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
response := RPCResponse{
JSONRPC: "2.0",
ID: 1,
Error: &RPCError{Code: -32000, Message: "proof invalid"},
}
json.NewEncoder(w).Encode(response)
}))
defer server.Close()
adapter := New(server.URL)
_, err := adapter.VerifyProof(context.Background(), ZKProof{Type: ProofGroth16, Data: "0x"})
if err == nil {
t.Error("expected error for RPC error response")
}
}
// TestShieldedTransferAllTypes tests all ZK transaction types
func TestShieldedTransferAllTypes(t *testing.T) {
types := []ZKTransactionType{
TxShieldedTransfer,
TxShield,
TxUnshield,
TxJoinSplit,
TxMint,
TxBurn,
}
for _, txType := range types {
t.Run(string(txType), func(t *testing.T) {
transfer := ShieldedTransfer{
TxID: "tx-" + string(txType),
Type: txType,
Proof: ZKProof{Type: ProofGroth16, Data: "0xproof"},
Fee: 100,
Timestamp: time.Now(),
}
data, err := json.Marshal(transfer)
if err != nil {
t.Fatalf("marshal: %v", err)
}
var decoded ShieldedTransfer
if err := json.Unmarshal(data, &decoded); err != nil {
t.Fatalf("unmarshal: %v", err)
}
if decoded.Type != txType {
t.Errorf("Type: got %s, want %s", decoded.Type, txType)
}
})
}
}
// TestRPCRequestMethodFormat tests RPC method format uses zvm prefix
func TestRPCRequestMethodFormat(t *testing.T) {
var capturedMethod string
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var req RPCRequest
json.NewDecoder(r.Body).Decode(&req)
capturedMethod = req.Method
response := RPCResponse{
JSONRPC: "2.0",
ID: 1,
Result: json.RawMessage(`[]`),
}
json.NewEncoder(w).Encode(response)
}))
defer server.Close()
adapter := New(server.URL)
adapter.GetRecentVertices(context.Background(), 10)
if capturedMethod != "zvm.getRecentVertices" {
t.Errorf("expected method zvm.getRecentVertices, got %s", capturedMethod)
}
}