go-lns/pkg/dns/resolve_test.go
Virgil 423a551896 refactor(dns): preserve alias-specific error names
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-02 12:50:29 +00:00

562 lines
16 KiB
Go

// SPDX-License-Identifier: EUPL-1.2
package dns
import (
"crypto/sha3"
"strings"
"testing"
core "dappco.re/go/core"
"dappco.re/go/lns/pkg/primitives"
)
func TestResolve(t *testing.T) {
svc := NewService()
got, err := svc.Resolve("Foo-Bar.lthn")
if err != nil {
t.Fatalf("Resolve returned error: %v", err)
}
want := sha3.Sum256([]byte("foo-bar"))
if got != want {
t.Fatalf("Resolve returned %x, want %x", got, want)
}
got, err = svc.Resolve([]byte("Foo-Bar.lthn"))
if err != nil {
t.Fatalf("Resolve([]byte) returned error: %v", err)
}
if got != want {
t.Fatalf("Resolve([]byte) returned %x, want %x", got, want)
}
}
func TestResolveNameAliases(t *testing.T) {
svc := NewService()
got, err := svc.ResolveName("Foo-Bar.lthn")
if err != nil {
t.Fatalf("ResolveName returned error: %v", err)
}
want := sha3.Sum256([]byte("foo-bar"))
if got != want {
t.Fatalf("ResolveName returned %x, want %x", got, want)
}
if _, err := svc.ResolveName(123); err == nil {
t.Fatal("ResolveName should reject unsupported input types")
}
}
func TestServiceGetAliases(t *testing.T) {
svc := NewService()
want := sha3.Sum256([]byte("foo-bar"))
resolveCases := []struct {
name string
fn func(any) (primitives.Hash, error)
}{
{name: "GetResolve", fn: svc.GetResolve},
{name: "GetHash", fn: svc.GetHash},
{name: "GetResolveString", fn: func(name any) (primitives.Hash, error) { return svc.GetResolveString(name.(string)) }},
{name: "GetResolveBinary", fn: func(name any) (primitives.Hash, error) { return svc.GetResolveBinary(name.([]byte)) }},
{name: "GetHashString", fn: func(name any) (primitives.Hash, error) { return svc.GetHashString(name.(string)) }},
{name: "GetHashBinary", fn: func(name any) (primitives.Hash, error) { return svc.GetHashBinary(name.([]byte)) }},
{name: "GetResolveName", fn: svc.GetResolveName},
{name: "GetHashName", fn: svc.GetHashName},
{name: "GetResolveByString", fn: func(name any) (primitives.Hash, error) { return svc.GetResolveByString(name.(string)) }},
{name: "GetResolveByBinary", fn: func(name any) (primitives.Hash, error) { return svc.GetResolveByBinary(name.([]byte)) }},
{name: "GetResolveByName", fn: svc.GetResolveByName},
{name: "GetHashByString", fn: func(name any) (primitives.Hash, error) { return svc.GetHashByString(name.(string)) }},
{name: "GetHashByBinary", fn: func(name any) (primitives.Hash, error) { return svc.GetHashByBinary(name.([]byte)) }},
{name: "GetHashByName", fn: svc.GetHashByName},
}
for _, tc := range resolveCases {
input := any("Foo-Bar.lthn")
if tc.name == "GetResolveBinary" || tc.name == "GetHashBinary" || tc.name == "GetResolveByBinary" || tc.name == "GetHashByBinary" {
input = []byte("Foo-Bar.lthn")
}
got, err := tc.fn(input)
if err != nil {
t.Fatalf("%s returned error: %v", tc.name, err)
}
if got != want {
t.Fatalf("%s returned %x, want %x", tc.name, got, want)
}
}
verifyCases := []struct {
name string
fn func(any) bool
}{
{name: "GetVerify", fn: svc.GetVerify},
{name: "GetVerifyString", fn: func(name any) bool { return svc.GetVerifyString(name.(string)) }},
{name: "GetVerifyBinary", fn: func(name any) bool { return svc.GetVerifyBinary(name.([]byte)) }},
{name: "GetVerifyName", fn: svc.GetVerifyName},
{name: "GetVerifyByString", fn: func(name any) bool { return svc.GetVerifyByString(name.(string)) }},
{name: "GetVerifyByBinary", fn: func(name any) bool { return svc.GetVerifyByBinary(name.([]byte)) }},
{name: "GetVerifyByName", fn: svc.GetVerifyByName},
}
for _, tc := range verifyCases {
input := any("Foo-Bar.lthn")
if tc.name == "GetVerifyBinary" || tc.name == "GetVerifyByBinary" {
input = []byte("Foo-Bar.lthn")
}
if !tc.fn(input) {
t.Fatalf("%s should accept canonical names", tc.name)
}
}
}
func TestPackageGetAliases(t *testing.T) {
want := sha3.Sum256([]byte("foo-bar"))
resolveCases := []struct {
name string
input any
fn func(any) (primitives.Hash, error)
}{
{name: "GetResolve", input: "Foo-Bar.lthn", fn: GetResolve},
{name: "GetHash", input: "Foo-Bar.lthn", fn: GetHash},
{name: "GetResolveString", input: "Foo-Bar.lthn", fn: func(name any) (primitives.Hash, error) { return GetResolveString(name.(string)) }},
{name: "GetResolveBinary", input: []byte("Foo-Bar.lthn"), fn: func(name any) (primitives.Hash, error) { return GetResolveBinary(name.([]byte)) }},
{name: "GetHashString", input: "Foo-Bar.lthn", fn: func(name any) (primitives.Hash, error) { return GetHashString(name.(string)) }},
{name: "GetHashBinary", input: []byte("Foo-Bar.lthn"), fn: func(name any) (primitives.Hash, error) { return GetHashBinary(name.([]byte)) }},
{name: "GetResolveName", input: "Foo-Bar.lthn", fn: GetResolveName},
{name: "GetHashName", input: "Foo-Bar.lthn", fn: GetHashName},
{name: "GetResolveByString", input: "Foo-Bar.lthn", fn: func(name any) (primitives.Hash, error) { return GetResolveByString(name.(string)) }},
{name: "GetResolveByBinary", input: []byte("Foo-Bar.lthn"), fn: func(name any) (primitives.Hash, error) { return GetResolveByBinary(name.([]byte)) }},
{name: "GetResolveByName", input: "Foo-Bar.lthn", fn: GetResolveByName},
{name: "GetHashByString", input: "Foo-Bar.lthn", fn: func(name any) (primitives.Hash, error) { return GetHashByString(name.(string)) }},
{name: "GetHashByBinary", input: []byte("Foo-Bar.lthn"), fn: func(name any) (primitives.Hash, error) { return GetHashByBinary(name.([]byte)) }},
{name: "GetHashByName", input: "Foo-Bar.lthn", fn: GetHashByName},
}
for _, tc := range resolveCases {
got, err := tc.fn(tc.input)
if err != nil {
t.Fatalf("%s returned error: %v", tc.name, err)
}
if got != want {
t.Fatalf("%s returned %x, want %x", tc.name, got, want)
}
}
verifyCases := []struct {
name string
input any
fn func(any) bool
}{
{name: "GetVerify", input: "Foo-Bar.lthn", fn: GetVerify},
{name: "GetVerifyString", input: "Foo-Bar.lthn", fn: func(name any) bool { return GetVerifyString(name.(string)) }},
{name: "GetVerifyBinary", input: []byte("Foo-Bar.lthn"), fn: func(name any) bool { return GetVerifyBinary(name.([]byte)) }},
{name: "GetVerifyName", input: "Foo-Bar.lthn", fn: GetVerifyName},
{name: "GetVerifyByString", input: "Foo-Bar.lthn", fn: func(name any) bool { return GetVerifyByString(name.(string)) }},
{name: "GetVerifyByBinary", input: []byte("Foo-Bar.lthn"), fn: func(name any) bool { return GetVerifyByBinary(name.([]byte)) }},
{name: "GetVerifyByName", input: "Foo-Bar.lthn", fn: GetVerifyByName},
}
for _, tc := range verifyCases {
if !tc.fn(tc.input) {
t.Fatalf("%s should accept canonical names", tc.name)
}
}
}
func TestHashAliases(t *testing.T) {
svc := NewService()
got, err := svc.Hash("Foo-Bar.lthn")
if err != nil {
t.Fatalf("Hash returned error: %v", err)
}
want := sha3.Sum256([]byte("foo-bar"))
if got != want {
t.Fatalf("Hash returned %x, want %x", got, want)
}
got, err = svc.HashString("Foo-Bar.lthn")
if err != nil {
t.Fatalf("HashString returned error: %v", err)
}
if got != want {
t.Fatalf("HashString returned %x, want %x", got, want)
}
got, err = svc.HashBinary([]byte("Foo-Bar.lthn"))
if err != nil {
t.Fatalf("HashBinary returned error: %v", err)
}
if got != want {
t.Fatalf("HashBinary returned %x, want %x", got, want)
}
got, err = svc.HashName("Foo-Bar.lthn")
if err != nil {
t.Fatalf("HashName returned error: %v", err)
}
if got != want {
t.Fatalf("HashName returned %x, want %x", got, want)
}
got, err = svc.HashByName([]byte("Foo-Bar.lthn"))
if err != nil {
t.Fatalf("HashByName returned error: %v", err)
}
if got != want {
t.Fatalf("HashByName returned %x, want %x", got, want)
}
if _, err := svc.Hash(123); err == nil {
t.Fatal("Hash should reject unsupported input types")
}
}
func TestResolveByNameAliases(t *testing.T) {
svc := NewService()
got, err := svc.ResolveByName("Foo-Bar.lthn")
if err != nil {
t.Fatalf("ResolveByName returned error: %v", err)
}
want := sha3.Sum256([]byte("foo-bar"))
if got != want {
t.Fatalf("ResolveByName returned %x, want %x", got, want)
}
if _, err := svc.ResolveByName(123); err == nil {
t.Fatal("ResolveByName should reject unsupported input types")
}
}
func TestResolveStringAndBinary(t *testing.T) {
svc := NewService()
got, err := svc.ResolveString("Foo-Bar.lthn")
if err != nil {
t.Fatalf("ResolveString returned error: %v", err)
}
want := sha3.Sum256([]byte("foo-bar"))
if got != want {
t.Fatalf("ResolveString returned %x, want %x", got, want)
}
got, err = svc.ResolveBinary([]byte("Foo-Bar.lthn"))
if err != nil {
t.Fatalf("ResolveBinary returned error: %v", err)
}
if got != want {
t.Fatalf("ResolveBinary returned %x, want %x", got, want)
}
}
func TestResolveByStringAndBinaryAliases(t *testing.T) {
svc := NewService()
got, err := svc.ResolveByString("Foo-Bar.lthn")
if err != nil {
t.Fatalf("ResolveByString returned error: %v", err)
}
want := sha3.Sum256([]byte("foo-bar"))
if got != want {
t.Fatalf("ResolveByString returned %x, want %x", got, want)
}
got, err = svc.ResolveByBinary([]byte("Foo-Bar.lthn"))
if err != nil {
t.Fatalf("ResolveByBinary returned error: %v", err)
}
if got != want {
t.Fatalf("ResolveByBinary returned %x, want %x", got, want)
}
}
func TestHashByStringAndBinaryAliases(t *testing.T) {
svc := NewService()
want := sha3.Sum256([]byte("foo-bar"))
got, err := svc.HashByString("Foo-Bar.lthn")
if err != nil {
t.Fatalf("HashByString returned error: %v", err)
}
if got != want {
t.Fatalf("HashByString returned %x, want %x", got, want)
}
got, err = svc.HashByBinary([]byte("Foo-Bar.lthn"))
if err != nil {
t.Fatalf("HashByBinary returned error: %v", err)
}
if got != want {
t.Fatalf("HashByBinary returned %x, want %x", got, want)
}
}
func TestResolveRejectsInvalidNames(t *testing.T) {
svc := NewService()
cases := []string{
"",
" foo-bar.lthn",
"foo-bar.lthn ",
"foo.bar.lthn",
"foo..lthn",
"foo.lthn..",
"foo-",
"test.lthn",
}
for _, name := range cases {
if _, err := svc.Resolve(name); err == nil {
t.Fatalf("Resolve(%q) should reject the name", name)
}
}
if _, err := svc.Resolve(123); err == nil {
t.Fatal("Resolve should reject unsupported input types")
}
}
func TestVerifyStringAndBinary(t *testing.T) {
svc := NewService()
if !svc.VerifyString("Foo-Bar.lthn") {
t.Fatal("VerifyString should accept canonical names")
}
if svc.VerifyString("foo.bar.lthn") {
t.Fatal("VerifyString should reject malformed names")
}
if !svc.VerifyBinary([]byte("Foo-Bar.lthn")) {
t.Fatal("VerifyBinary should accept canonical names")
}
if svc.VerifyBinary([]byte("foo.bar.lthn")) {
t.Fatal("VerifyBinary should reject malformed names")
}
}
func TestVerifyByStringAndBinaryAliases(t *testing.T) {
svc := NewService()
if !svc.VerifyByString("Foo-Bar.lthn") {
t.Fatal("VerifyByString should accept canonical names")
}
if svc.VerifyByString("foo.bar.lthn") {
t.Fatal("VerifyByString should reject malformed names")
}
if !svc.VerifyByBinary([]byte("Foo-Bar.lthn")) {
t.Fatal("VerifyByBinary should accept canonical names")
}
if svc.VerifyByBinary([]byte("foo.bar.lthn")) {
t.Fatal("VerifyByBinary should reject malformed names")
}
}
func TestVerify(t *testing.T) {
svc := NewService()
cases := []struct {
name any
ok bool
}{
{name: "Foo-Bar.lthn", ok: true},
{name: []byte("Foo-Bar.lthn"), ok: true},
{name: "foo.bar.lthn", ok: false},
{name: "foo-", ok: false},
{name: 123, ok: false},
}
for _, tc := range cases {
if got := svc.Verify(tc.name); got != tc.ok {
t.Fatalf("Verify(%v) = %v, want %v", tc.name, got, tc.ok)
}
}
}
func TestVerifyNameAliases(t *testing.T) {
svc := NewService()
if !svc.VerifyName("Foo-Bar.lthn") {
t.Fatal("VerifyName should accept canonical names")
}
if svc.VerifyName("foo.bar.lthn") {
t.Fatal("VerifyName should reject malformed names")
}
if svc.VerifyName(123) {
t.Fatal("VerifyName should reject unsupported input types")
}
}
func TestVerifyByNameAliases(t *testing.T) {
svc := NewService()
if !svc.VerifyByName("Foo-Bar.lthn") {
t.Fatal("VerifyByName should accept canonical names")
}
if svc.VerifyByName("foo.bar.lthn") {
t.Fatal("VerifyByName should reject malformed names")
}
if svc.VerifyByName(123) {
t.Fatal("VerifyByName should reject unsupported input types")
}
}
func TestCoreAccessors(t *testing.T) {
c := &core.Core{}
svc := NewService(WithCore(c))
if svc.Core() != c {
t.Fatal("Core should return the configured Core instance")
}
if svc.GetCore() != c {
t.Fatal("GetCore should alias Core")
}
}
func TestNewServiceNilOptions(t *testing.T) {
svc := NewService(nil, WithCore(nil))
if svc == nil {
t.Fatal("NewService should return a service even when options are nil")
}
if svc.Core() != nil {
t.Fatal("NewService(nil, WithCore(nil)) should preserve a nil Core")
}
}
func TestWithCoreNilSafety(t *testing.T) {
var svc *Service
WithCore(nil)(svc)
WithCore(&core.Core{})(nil)
}
func TestServiceName(t *testing.T) {
svc := NewService()
if got := svc.ServiceName(); got != ServiceName {
t.Fatalf("ServiceName() = %q, want %q", got, ServiceName)
}
if got := svc.GetServiceName(); got != ServiceName {
t.Fatalf("GetServiceName() = %q, want %q", got, ServiceName)
}
if got := GetServiceName(); got != ServiceName {
t.Fatalf("package GetServiceName() = %q, want %q", got, ServiceName)
}
}
func TestPackageLevelResolveAndVerifyAliases(t *testing.T) {
want := sha3.Sum256([]byte("foo-bar"))
resolveCases := []struct {
name string
fn func(any) (primitives.Hash, error)
}{
{name: "Resolve", fn: Resolve},
{name: "Hash", fn: Hash},
{name: "ResolveName", fn: ResolveName},
{name: "HashName", fn: HashName},
{name: "ResolveByName", fn: ResolveByName},
{name: "HashByName", fn: HashByName},
{name: "ResolveString", fn: func(name any) (primitives.Hash, error) { return ResolveString(name.(string)) }},
{name: "ResolveBinary", fn: func(name any) (primitives.Hash, error) { return ResolveBinary(name.([]byte)) }},
{name: "HashString", fn: func(name any) (primitives.Hash, error) { return HashString(name.(string)) }},
{name: "HashBinary", fn: func(name any) (primitives.Hash, error) { return HashBinary(name.([]byte)) }},
{name: "ResolveByString", fn: func(name any) (primitives.Hash, error) { return ResolveByString(name.(string)) }},
{name: "ResolveByBinary", fn: func(name any) (primitives.Hash, error) { return ResolveByBinary(name.([]byte)) }},
{name: "HashByString", fn: func(name any) (primitives.Hash, error) { return HashByString(name.(string)) }},
{name: "HashByBinary", fn: func(name any) (primitives.Hash, error) { return HashByBinary(name.([]byte)) }},
}
for _, tc := range resolveCases {
input := any("Foo-Bar.lthn")
if tc.name == "ResolveBinary" || tc.name == "HashBinary" || tc.name == "ResolveByBinary" || tc.name == "HashByBinary" {
input = []byte("Foo-Bar.lthn")
}
got, err := tc.fn(input)
if err != nil {
t.Fatalf("%s returned error: %v", tc.name, err)
}
if got != want {
t.Fatalf("%s returned %x, want %x", tc.name, got, want)
}
}
if _, err := Resolve(123); err == nil || !strings.Contains(err.Error(), "dns.Resolve") {
t.Fatalf("Resolve should reject unsupported input types with a dns.Resolve error, got %v", err)
}
if _, err := Hash(123); err == nil || !strings.Contains(err.Error(), "dns.Hash") {
t.Fatalf("Hash should reject unsupported input types with a dns.Hash error, got %v", err)
}
verifyCases := []struct {
name string
fn func(any) bool
}{
{name: "Verify", fn: Verify},
{name: "VerifyName", fn: VerifyName},
{name: "VerifyByName", fn: VerifyByName},
{name: "VerifyString", fn: func(name any) bool { return VerifyString(name.(string)) }},
{name: "VerifyBinary", fn: func(name any) bool { return VerifyBinary(name.([]byte)) }},
{name: "VerifyByString", fn: func(name any) bool { return VerifyByString(name.(string)) }},
{name: "VerifyByBinary", fn: func(name any) bool { return VerifyByBinary(name.([]byte)) }},
}
for _, tc := range verifyCases {
input := any("Foo-Bar.lthn")
if tc.name == "VerifyBinary" || tc.name == "VerifyByBinary" {
input = []byte("Foo-Bar.lthn")
}
if !tc.fn(input) {
t.Fatalf("%s should accept canonical names", tc.name)
}
if tc.name == "VerifyBinary" || tc.name == "VerifyByBinary" {
input = []byte("foo.bar.lthn")
} else {
input = "foo.bar.lthn"
}
if tc.fn(input) {
t.Fatalf("%s should reject malformed names", tc.name)
}
}
}