@ -4,10 +4,14 @@
package appc
import (
"encoding/json"
"net/netip"
"reflect"
"testing"
"tailscale.com/tailcfg"
"tailscale.com/types/appctype"
"tailscale.com/types/opt"
)
// TestHandleConnectorTransitIPRequestZeroLength tests that if sent a
@ -186,3 +190,122 @@ func TestTransitIPTargetUnknownTIP(t *testing.T) {
t . Fatalf ( "Unknown transit addr, want: %v, got %v" , want , got )
}
}
func TestPickSplitDNSPeers ( t * testing . T ) {
getBytesForAttr := func ( name string , domains [ ] string , tags [ ] string ) [ ] byte {
attr := appctype . AppConnectorAttr {
Name : name ,
Domains : domains ,
Connectors : tags ,
}
bs , err := json . Marshal ( attr )
if err != nil {
t . Fatalf ( "test setup: %v" , err )
}
return bs
}
appOneBytes := getBytesForAttr ( "app1" , [ ] string { "example.com" } , [ ] string { "tag:one" } )
appTwoBytes := getBytesForAttr ( "app2" , [ ] string { "a.example.com" } , [ ] string { "tag:two" } )
appThreeBytes := getBytesForAttr ( "app3" , [ ] string { "woo.b.example.com" , "hoo.b.example.com" } , [ ] string { "tag:three1" , "tag:three2" } )
appFourBytes := getBytesForAttr ( "app4" , [ ] string { "woo.b.example.com" , "c.example.com" } , [ ] string { "tag:four1" , "tag:four2" } )
makeNodeView := func ( id tailcfg . NodeID , name string , tags [ ] string ) tailcfg . NodeView {
return ( & tailcfg . Node {
ID : id ,
Name : name ,
Tags : tags ,
Hostinfo : ( & tailcfg . Hostinfo { AppConnector : opt . NewBool ( true ) } ) . View ( ) ,
} ) . View ( )
}
nvp1 := makeNodeView ( 1 , "p1" , [ ] string { "tag:one" } )
nvp2 := makeNodeView ( 2 , "p2" , [ ] string { "tag:four1" , "tag:four2" } )
nvp3 := makeNodeView ( 3 , "p3" , [ ] string { "tag:two" , "tag:three1" } )
nvp4 := makeNodeView ( 4 , "p4" , [ ] string { "tag:two" , "tag:three2" , "tag:four2" } )
for _ , tt := range [ ] struct {
name string
want map [ string ] [ ] tailcfg . NodeView
peers [ ] tailcfg . NodeView
config [ ] tailcfg . RawMessage
} {
{
name : "empty" ,
} ,
{
name : "bad-config" , // bad config should return a nil map rather than error.
config : [ ] tailcfg . RawMessage { tailcfg . RawMessage ( ` hey ` ) } ,
} ,
{
name : "no-peers" ,
config : [ ] tailcfg . RawMessage { tailcfg . RawMessage ( appOneBytes ) } ,
} ,
{
name : "peers-that-are-not-connectors" ,
config : [ ] tailcfg . RawMessage { tailcfg . RawMessage ( appOneBytes ) } ,
peers : [ ] tailcfg . NodeView {
( & tailcfg . Node {
ID : 5 ,
Name : "p5" ,
Tags : [ ] string { "tag:one" } ,
} ) . View ( ) ,
( & tailcfg . Node {
ID : 6 ,
Name : "p6" ,
Tags : [ ] string { "tag:one" } ,
} ) . View ( ) ,
} ,
} ,
{
name : "peers-that-dont-match-tags" ,
config : [ ] tailcfg . RawMessage { tailcfg . RawMessage ( appOneBytes ) } ,
peers : [ ] tailcfg . NodeView {
makeNodeView ( 5 , "p5" , [ ] string { "tag:seven" } ) ,
makeNodeView ( 6 , "p6" , nil ) ,
} ,
} ,
{
name : "matching-tagged-connector-peers" ,
config : [ ] tailcfg . RawMessage {
tailcfg . RawMessage ( appOneBytes ) ,
tailcfg . RawMessage ( appTwoBytes ) ,
tailcfg . RawMessage ( appThreeBytes ) ,
tailcfg . RawMessage ( appFourBytes ) ,
} ,
peers : [ ] tailcfg . NodeView {
nvp1 ,
nvp2 ,
nvp3 ,
nvp4 ,
makeNodeView ( 5 , "p5" , nil ) ,
} ,
want : map [ string ] [ ] tailcfg . NodeView {
// p5 has no matching tags and so doesn't appear
"example.com" : { nvp1 } ,
"a.example.com" : { nvp3 , nvp4 } ,
"woo.b.example.com" : { nvp2 , nvp3 , nvp4 } ,
"hoo.b.example.com" : { nvp3 , nvp4 } ,
"c.example.com" : { nvp2 , nvp4 } ,
} ,
} ,
} {
t . Run ( tt . name , func ( t * testing . T ) {
selfNode := & tailcfg . Node { }
if tt . config != nil {
selfNode . CapMap = tailcfg . NodeCapMap {
tailcfg . NodeCapability ( AppConnectorsExperimentalAttrName ) : tt . config ,
}
}
selfView := selfNode . View ( )
peers := map [ tailcfg . NodeID ] tailcfg . NodeView { }
for _ , p := range tt . peers {
peers [ p . ID ( ) ] = p
}
got := PickSplitDNSPeers ( func ( _ tailcfg . NodeCapability ) bool {
return true
} , selfView , peers )
if ! reflect . DeepEqual ( got , tt . want ) {
t . Fatalf ( "got %v, want %v" , got , tt . want )
}
} )
}
}