Files

238 lines
5.9 KiB
Go

// Copyright (C) 2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package graph
import (
"encoding/binary"
"encoding/json"
"testing"
"github.com/luxfi/database/memdb"
gvm "github.com/luxfi/precompile/graph/graphvm"
"github.com/stretchr/testify/require"
)
func TestSetGraphVMClient(t *testing.T) {
// Save original state
origClient := GraphContractInstance.precompile.client
defer func() {
GraphContractInstance.precompile.client = origClient
}()
db := memdb.New()
defer db.Close()
// Set the client
SetGraphVMClient(db, nil)
// Verify client was set
require.NotNil(t, GraphContractInstance.precompile.client)
client, ok := GraphContractInstance.precompile.client.(*GraphVMClient)
require.True(t, ok)
require.NotNil(t, client.executor)
}
func TestSetGraphVMClientWithChainID(t *testing.T) {
// Save original state
origClient := GraphContractInstance.precompile.client
defer func() {
GraphContractInstance.precompile.client = origClient
}()
db := memdb.New()
defer db.Close()
// Set the client with chain ID
SetGraphVMClientWithChainID(db, nil, 96369)
// Verify client was set with chain ID
require.NotNil(t, GraphContractInstance.precompile.client)
client, ok := GraphContractInstance.precompile.client.(*GraphVMClient)
require.True(t, ok)
require.Equal(t, uint64(96369), client.ChainID())
}
func TestGraphConfigKey(t *testing.T) {
cfg := &GraphConfig{}
require.Equal(t, ConfigKey, cfg.Key())
require.Equal(t, "graphConfig", cfg.Key())
}
func TestGraphConfigTimestamp(t *testing.T) {
// Test nil timestamp
cfg := &GraphConfig{}
require.Nil(t, cfg.Timestamp())
// Test with timestamp
ts := uint64(12345)
cfg.Upgrade.BlockTimestamp = &ts
require.Equal(t, &ts, cfg.Timestamp())
}
func TestGraphConfigIsDisabled(t *testing.T) {
cfg := &GraphConfig{}
require.False(t, cfg.IsDisabled())
cfg.Upgrade.Disable = true
require.True(t, cfg.IsDisabled())
}
func TestGraphConfigEqual(t *testing.T) {
cfg1 := &GraphConfig{
GChainEndpoint: "http://localhost:9650",
QueryTimeout: 5,
MaxCacheSize: 1000,
}
cfg2 := &GraphConfig{
GChainEndpoint: "http://localhost:9650",
QueryTimeout: 5,
MaxCacheSize: 1000,
}
cfg3 := &GraphConfig{
GChainEndpoint: "http://localhost:9651",
QueryTimeout: 5,
MaxCacheSize: 1000,
}
// Same config
require.True(t, cfg1.Equal(cfg2))
// Different config
require.False(t, cfg1.Equal(cfg3))
// Wrong type
require.False(t, cfg1.Equal(nil))
}
func TestGraphConfigVerify(t *testing.T) {
cfg := &GraphConfig{}
err := cfg.Verify(nil)
require.NoError(t, err)
}
func TestGraphQLContractRun(t *testing.T) {
// Save original state
origClient := GraphContractInstance.precompile.client
defer func() {
GraphContractInstance.precompile.client = origClient
}()
// Set up real GraphVM client
db := memdb.New()
defer db.Close()
config := &gvm.GConfig{
MaxQueryDepth: 10,
MaxResultSize: 1 << 20,
QueryTimeoutMs: 5000,
}
SetGraphVMClient(db, config)
// Create input for query method (selector 0x01)
req := QueryRequest{
Query: `query { chainInfo { vmName version readOnly } }`,
}
reqBytes, err := json.Marshal(req)
require.NoError(t, err)
input := make([]byte, 4+len(reqBytes))
binary.BigEndian.PutUint32(input[:4], 0x01) // query method selector
copy(input[4:], reqBytes)
// Run the contract
result, remainingGas, runErr := GraphContractInstance.Run(
nil, // accessibleState
ContractGraphQLAddress, // caller
ContractGraphQLAddress, // addr
input, // input
1_000_000, // suppliedGas
true, // readOnly
)
require.NoError(t, runErr)
require.NotNil(t, result)
require.Less(t, remainingGas, uint64(1_000_000))
// Parse response
var response map[string]any
err = json.Unmarshal(result, &response)
require.NoError(t, err)
chainInfo, ok := response["chainInfo"].(map[string]any)
require.True(t, ok)
require.Equal(t, "graphvm", chainInfo["vmName"])
}
func TestGraphQLContractRunOutOfGas(t *testing.T) {
input := make([]byte, 8)
binary.BigEndian.PutUint32(input[:4], 0x01)
_, _, err := GraphContractInstance.Run(
nil,
ContractGraphQLAddress,
ContractGraphQLAddress,
input,
100, // Not enough gas
true,
)
require.Error(t, err)
require.Contains(t, err.Error(), "out of gas")
}
func TestGraphQLContractRunGetStats(t *testing.T) {
// Create input for getStats method (selector 0x03)
input := make([]byte, 4)
binary.BigEndian.PutUint32(input[:4], 0x03)
result, _, err := GraphContractInstance.Run(
nil,
ContractGraphQLAddress,
ContractGraphQLAddress,
input,
1_000_000,
true,
)
require.NoError(t, err)
require.NotNil(t, result)
// Parse stats response
var stats QueryStats
err = json.Unmarshal(result, &stats)
require.NoError(t, err)
}
func TestGraphQLContractRunInvalidInput(t *testing.T) {
// Too short input
_, _, err := GraphContractInstance.Run(
nil,
ContractGraphQLAddress,
ContractGraphQLAddress,
[]byte{1, 2}, // Only 2 bytes
1_000_000,
true,
)
require.Error(t, err)
}
func TestModuleRegistration(t *testing.T) {
// Module should be registered
require.Equal(t, ConfigKey, Module.ConfigKey)
require.Equal(t, ContractGraphQLAddress, Module.Address)
require.Equal(t, GraphContractInstance, Module.Contract)
require.NotNil(t, Module.Configurator)
}
func TestContractAddresses(t *testing.T) {
// Verify contract addresses are in Graph/Query range (0x0500-0x05FF)
// Note: Hashing precompiles (Poseidon2, Pedersen, Blake3) use 0x0500...0001-0004
// Graph precompiles start at 0x0500...0010 to avoid conflicts
require.Equal(t, "0x0500000000000000000000000000000000000010", ContractGraphQLAddress.Hex())
require.Equal(t, "0x0500000000000000000000000000000000000011", ContractSubscribeAddress.Hex())
require.Equal(t, "0x0500000000000000000000000000000000000012", ContractCacheAddress.Hex())
require.Equal(t, "0x0500000000000000000000000000000000000013", ContractIndexAddress.Hex())
}