xmr-remote-nodes/internal/monero/monero_test.go
Christian Ditaputratama 5e2ab83295
feat: Accept i2p naming service hostname #148
Please note that this naming service validation only validates simple
length and allowed characters. Advanced validation such as
internationalized domain name (IDN) is not implemented.

To minimize abuse, I also set minimum length of submitted i2p naming
service address to 5 characters. If someone have an address of 4
characters or less, let them open an issue or create a pull request.
2024-11-08 00:23:07 +07:00

244 lines
5.9 KiB
Go

package monero
import (
"os"
"reflect"
"strconv"
"testing"
"github.com/ditatompel/xmr-remote-nodes/internal/config"
"github.com/ditatompel/xmr-remote-nodes/internal/database"
"github.com/ditatompel/xmr-remote-nodes/internal/paging"
)
var testMySQL = true
// TODO: Add database test table and then clean it up
func init() {
// load test db config from OS environment variable
//
// Example:
// TEST_DB_HOST=127.0.0.1 \
// TEST_DB_PORT=3306 \
// TEST_DB_USER=testuser \
// TEST_DB_PASSWORD=testpass \
// TEST_DB_NAME=testdb go test ./... -v
//
// To run benchmark only, add `-bench=. -run=^#` to the `go test` command
config.DBCfg().Host = os.Getenv("TEST_DB_HOST")
config.DBCfg().Port, _ = strconv.Atoi(os.Getenv("TEST_DB_PORT"))
config.DBCfg().User = os.Getenv("TEST_DB_USER")
config.DBCfg().Password = os.Getenv("TEST_DB_PASSWORD")
config.DBCfg().Name = os.Getenv("TEST_DB_NAME")
if err := database.ConnectDB(); err != nil {
testMySQL = false
}
}
// Single test:
// go test -race ./internal/monero -run=TestQueryNodes_toSQL -v
func TestQueryNodes_toSQL(t *testing.T) {
tests := []struct {
name string
query QueryNodes
wantArgs []interface{}
wantWhere string
wantSortBy string
wantSortDir string
}{
{
name: "Default query",
query: QueryNodes{
Paging: paging.Paging{
Limit: 10,
Page: 1,
SortBy: "last_checked",
SortDirection: "desc",
},
Host: "",
Nettype: "any",
Protocol: "any",
CC: "any",
Status: -1,
CORS: "",
},
wantArgs: []interface{}{},
wantWhere: "",
wantSortBy: "last_checked",
wantSortDir: "DESC",
},
{
name: "With host query",
query: QueryNodes{
Paging: paging.Paging{
Limit: 10,
Page: 1,
SortBy: "last_checked",
SortDirection: "desc",
},
Host: "test",
Nettype: "any",
Protocol: "any",
CC: "any",
Status: -1,
CORS: "",
},
wantArgs: []interface{}{"%test%", "%test%"},
wantWhere: "WHERE (hostname LIKE ? OR ip_addr LIKE ?)",
wantSortBy: "last_checked",
wantSortDir: "DESC",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotArgs, gotWhere := tt.query.toSQL()
if !equalArgs(gotArgs, tt.wantArgs) {
t.Errorf("QueryNodes.toSQL() gotArgs = %v, want %v", gotArgs, tt.wantArgs)
}
if gotWhere != tt.wantWhere {
t.Errorf("QueryNodes.toSQL() gotWhere = %v, want %v", gotWhere, tt.wantWhere)
}
if tt.query.SortBy != tt.wantSortBy {
t.Errorf("QueryNodes.toSQL() gotSortBy = %v, want %v", tt.query.SortBy, tt.wantSortBy)
}
if tt.query.SortDirection != tt.wantSortDir {
t.Errorf("QueryNodes.toSQL() gotSortDir = %v, want %v", tt.query.SortDirection, tt.wantSortDir)
}
})
}
}
// Single bench test:
// go test ./internal/monero -bench QueryNodes_toSQL -benchmem -run=^$ -v
func Benchmark_QueryNodes_toSQL(b *testing.B) {
q := QueryNodes{
Paging: paging.Paging{
Limit: 10,
Page: 1,
SortBy: "last_checked",
SortDirection: "desc",
},
Host: "test",
Nettype: "any",
Protocol: "any",
CC: "any",
Status: -1,
CORS: "",
}
for i := 0; i < b.N; i++ {
_, _ = q.toSQL()
}
}
// Single test:
// go test -race ./internal/monero -run=TestValidTorHostname -v
func TestValidTorHostname(t *testing.T) {
tests := []struct {
name string
host string
wantValid bool
}{
{
name: "Empty host",
host: "",
wantValid: false,
},
{
name: "Valid tor host",
host: "cakexmrl7bonq7ovjka5kuwuyd3f7qnkz6z6s6dmsy3uckwra7bvggyd.onion",
wantValid: true,
},
{
name: "Valid tor host with subdomain",
host: "just-test.cakexmrl7bonq7ovjka5kuwuyd3f7qnkz6z6s6dmsy3uckwra7bvggyd.onion",
wantValid: true,
},
{
name: "Invalid host",
host: "test.com",
wantValid: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if r := validTorHostname(tt.host); r != tt.wantValid {
t.Errorf("ValidTorHostname() error = %v, wantValid %v", r, tt.wantValid)
}
})
}
}
// Single bench test:
// go test ./internal/monero -bench validTorHostname -benchmem -run=^$ -v
func Benchmark_validTorHostname(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = validTorHostname("cakexmrl7bonq7ovjka5kuwuyd3f7qnkz6z6s6dmsy3uckwra7bvggyd.onion")
}
}
// Single test:
// go test -race ./internal/monero -run=TestValidI2PHostname -v
func TestValidI2PHostname(t *testing.T) {
tests := []struct {
name string
host string
wantValid bool
}{
{
name: "Empty host",
host: "",
wantValid: false,
},
{
name: "Valid b32 i2p host (old format)",
host: "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrst234567.b32.i2p",
wantValid: true,
},
{
name: "Invalid b32 i2p host (old format)",
host: "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrst123456.b32.i2p",
wantValid: false,
},
{
name: "Valid naming service i2p host",
host: "i2p-projekt.i2p",
wantValid: true,
},
{
name: "clearnet domain",
host: "test.com",
wantValid: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if r := validI2PHostname(tt.host); r != tt.wantValid {
t.Errorf("ValidTorHostname() error = %v, wantValid %v", r, tt.wantValid)
}
})
}
}
// Single bench test:
// go test ./internal/monero -bench validI2PHostname -benchmem -run=^$ -v
func Benchmark_validI2PHostname(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = validTorHostname("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrst234567.b32.i2p")
}
}
// equalArgs is helper function for testing.
//
// This returns true if two slices of interface{} are equal.
func equalArgs(a, b []interface{}) bool {
if len(a) != len(b) {
return false
}
for i, v := range a {
if !reflect.DeepEqual(v, b[i]) {
return false
}
}
return true
}