added simple websocket test
- bump to v0.21.1
This commit is contained in:
71
cmd/relay-tester/README.md
Normal file
71
cmd/relay-tester/README.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# relay-tester
|
||||
|
||||
A command-line tool for testing Nostr relay implementations against the NIP-01 specification and related NIPs.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
relay-tester -url <relay-url> [options]
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
- `-url` (required): Relay websocket URL (e.g., `ws://127.0.0.1:3334` or `wss://relay.example.com`)
|
||||
- `-test <name>`: Run a specific test by name (default: run all tests)
|
||||
- `-json`: Output results in JSON format
|
||||
- `-v`: Verbose output (shows additional info for each test)
|
||||
- `-list`: List all available tests and exit
|
||||
|
||||
## Examples
|
||||
|
||||
### Run all tests against a local relay:
|
||||
```bash
|
||||
relay-tester -url ws://127.0.0.1:3334
|
||||
```
|
||||
|
||||
### Run all tests with verbose output:
|
||||
```bash
|
||||
relay-tester -url ws://127.0.0.1:3334 -v
|
||||
```
|
||||
|
||||
### Run a specific test:
|
||||
```bash
|
||||
relay-tester -url ws://127.0.0.1:3334 -test "Publishes basic event"
|
||||
```
|
||||
|
||||
### Output results as JSON:
|
||||
```bash
|
||||
relay-tester -url ws://127.0.0.1:3334 -json
|
||||
```
|
||||
|
||||
### List all available tests:
|
||||
```bash
|
||||
relay-tester -list
|
||||
```
|
||||
|
||||
## Exit Codes
|
||||
|
||||
- `0`: All required tests passed
|
||||
- `1`: One or more required tests failed, or an error occurred
|
||||
|
||||
## Test Categories
|
||||
|
||||
The relay-tester runs tests covering:
|
||||
|
||||
- **Basic Event Operations**: Publishing, finding by ID/author/kind/tags
|
||||
- **Filtering**: Time ranges, limits, multiple filters, scrape queries
|
||||
- **Replaceable Events**: Metadata and contact list replacement
|
||||
- **Parameterized Replaceable Events**: Addressable events with `d` tags
|
||||
- **Event Deletion**: Deletion events (NIP-09)
|
||||
- **Ephemeral Events**: Event handling for ephemeral kinds
|
||||
- **EOSE Handling**: End of stored events signaling
|
||||
- **Event Validation**: Signature verification, ID hash verification
|
||||
- **JSON Compliance**: NIP-01 JSON escape sequences
|
||||
|
||||
## Notes
|
||||
|
||||
- Tests are run in dependency order (some tests depend on others)
|
||||
- Required tests must pass for the relay to be considered compliant
|
||||
- Optional tests may fail without affecting overall compliance
|
||||
- The tool connects to the relay using WebSocket and runs tests sequentially
|
||||
|
||||
160
cmd/relay-tester/main.go
Normal file
160
cmd/relay-tester/main.go
Normal file
@@ -0,0 +1,160 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"lol.mleku.dev/log"
|
||||
relaytester "next.orly.dev/relay-tester"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var (
|
||||
relayURL = flag.String("url", "", "relay websocket URL (required, e.g., ws://127.0.0.1:3334)")
|
||||
testName = flag.String("test", "", "run specific test by name (default: run all tests)")
|
||||
jsonOut = flag.Bool("json", false, "output results in JSON format")
|
||||
verbose = flag.Bool("v", false, "verbose output")
|
||||
listTests = flag.Bool("list", false, "list all available tests and exit")
|
||||
)
|
||||
flag.Parse()
|
||||
|
||||
if *listTests {
|
||||
listAllTests()
|
||||
return
|
||||
}
|
||||
|
||||
if *relayURL == "" {
|
||||
log.E.F("required flag: -url (relay websocket URL)")
|
||||
flag.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Validate URL format
|
||||
if !strings.HasPrefix(*relayURL, "ws://") && !strings.HasPrefix(*relayURL, "wss://") {
|
||||
log.E.F("URL must start with ws:// or wss://")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Create test suite
|
||||
if *verbose {
|
||||
log.I.F("Creating test suite for %s...", *relayURL)
|
||||
}
|
||||
suite, err := relaytester.NewTestSuite(*relayURL)
|
||||
if err != nil {
|
||||
log.E.F("failed to create test suite: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Run tests
|
||||
var results []relaytester.TestResult
|
||||
if *testName != "" {
|
||||
if *verbose {
|
||||
log.I.F("Running test: %s", *testName)
|
||||
}
|
||||
result, err := suite.RunTest(*testName)
|
||||
if err != nil {
|
||||
log.E.F("failed to run test %s: %v", *testName, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
results = []relaytester.TestResult{result}
|
||||
} else {
|
||||
if *verbose {
|
||||
log.I.F("Running all tests...")
|
||||
}
|
||||
if results, err = suite.Run(); err != nil {
|
||||
log.E.F("failed to run tests: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Output results
|
||||
if *jsonOut {
|
||||
jsonOutput, err := relaytester.FormatJSON(results)
|
||||
if err != nil {
|
||||
log.E.F("failed to format JSON: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println(jsonOutput)
|
||||
} else {
|
||||
outputResults(results, *verbose)
|
||||
}
|
||||
|
||||
// Check exit code
|
||||
hasRequiredFailures := false
|
||||
for _, result := range results {
|
||||
if result.Required && !result.Pass {
|
||||
hasRequiredFailures = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if hasRequiredFailures {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func outputResults(results []relaytester.TestResult, verbose bool) {
|
||||
passed := 0
|
||||
failed := 0
|
||||
requiredFailed := 0
|
||||
|
||||
for _, result := range results {
|
||||
if result.Pass {
|
||||
passed++
|
||||
if verbose {
|
||||
fmt.Printf("PASS: %s", result.Name)
|
||||
if result.Info != "" {
|
||||
fmt.Printf(" - %s", result.Info)
|
||||
}
|
||||
fmt.Println()
|
||||
} else {
|
||||
fmt.Printf("PASS: %s\n", result.Name)
|
||||
}
|
||||
} else {
|
||||
failed++
|
||||
if result.Required {
|
||||
requiredFailed++
|
||||
fmt.Printf("FAIL (required): %s", result.Name)
|
||||
} else {
|
||||
fmt.Printf("FAIL (optional): %s", result.Name)
|
||||
}
|
||||
if result.Info != "" {
|
||||
fmt.Printf(" - %s", result.Info)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
fmt.Println("Test Summary:")
|
||||
fmt.Printf(" Total: %d\n", len(results))
|
||||
fmt.Printf(" Passed: %d\n", passed)
|
||||
fmt.Printf(" Failed: %d\n", failed)
|
||||
fmt.Printf(" Required Failed: %d\n", requiredFailed)
|
||||
}
|
||||
|
||||
func listAllTests() {
|
||||
// Create a dummy test suite to get the list of tests
|
||||
suite, err := relaytester.NewTestSuite("ws://127.0.0.1:0")
|
||||
if err != nil {
|
||||
log.E.F("failed to create test suite: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Println("Available tests:")
|
||||
fmt.Println()
|
||||
|
||||
testNames := suite.ListTests()
|
||||
testInfo := suite.GetTestNames()
|
||||
|
||||
for _, name := range testNames {
|
||||
required := ""
|
||||
if testInfo[name] {
|
||||
required = " (required)"
|
||||
}
|
||||
fmt.Printf(" - %s%s\n", name, required)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user