107 lines
3.5 KiB
Go
107 lines
3.5 KiB
Go
package routeros
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"slices"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestSyncOwnsOnlyPrefixedRules(t *testing.T) {
|
|
requests := make([]string, 0)
|
|
transport := roundTripFunc(func(request *http.Request) (*http.Response, error) {
|
|
username, password, ok := request.BasicAuth()
|
|
if !ok || username != "operator" || password != "secret" {
|
|
t.Errorf("wrong Basic Auth credentials")
|
|
}
|
|
requests = append(requests, request.Method+" "+request.URL.Path)
|
|
body := `{}`
|
|
if request.Method == http.MethodGet {
|
|
encoded, err := json.Marshal([]map[string]string{
|
|
{".id": "*1", "comment": "manual rule", "chain": "dstnat"},
|
|
{".id": "*2", "comment": "k8s-mikrotik/games/factorio", "chain": "dstnat", "action": "dst-nat", "protocol": "udp", "dst-port": "34197", "to-addresses": "192.168.0.99", "to-ports": "34197"},
|
|
{".id": "*3", "comment": "k8s-mikrotik/old/gone", "chain": "dstnat", "action": "dst-nat"},
|
|
{".id": "*4", "comment": "k8s-mikrotik/pending/service", "chain": "dstnat", "action": "dst-nat"},
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
body = string(encoded)
|
|
}
|
|
return &http.Response{
|
|
StatusCode: http.StatusOK,
|
|
Status: "200 OK",
|
|
Header: make(http.Header),
|
|
Body: io.NopCloser(strings.NewReader(body)),
|
|
Request: request,
|
|
}, nil
|
|
})
|
|
|
|
client, err := NewClient("https://router.test", "operator", "secret", TLSConfig{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
client.http.Transport = transport
|
|
desired := []Rule{
|
|
{Comment: "k8s-mikrotik/games/factorio", Protocol: "udp", DestinationPort: "34197", ToAddress: "192.168.0.17", ToPort: "34197"},
|
|
{Comment: "k8s-mikrotik/new/service", Protocol: "tcp", DestinationPort: "443", ToAddress: "192.168.0.18", ToPort: "8443"},
|
|
}
|
|
count, err := client.Sync(context.Background(), desired, map[string]struct{}{"k8s-mikrotik/pending/service": {}})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if count != 3 {
|
|
t.Fatalf("managed count = %d, want 3", count)
|
|
}
|
|
|
|
got := slices.Clone(requests)
|
|
want := []string{
|
|
"GET /rest/ip/firewall/nat",
|
|
"PATCH /rest/ip/firewall/nat/*2",
|
|
"PUT /rest/ip/firewall/nat",
|
|
"DELETE /rest/ip/firewall/nat/*3",
|
|
}
|
|
if !slices.Equal(got, want) {
|
|
t.Fatalf("requests = %#v, want %#v", got, want)
|
|
}
|
|
for _, request := range got {
|
|
if strings.Contains(request, "*1") || strings.Contains(request, "*4") {
|
|
t.Fatalf("operator touched a manual or preserved rule: %s", request)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCommentPreservedByServicePrefix(t *testing.T) {
|
|
preserve := map[string]struct{}{"k8s-mikrotik/default/traefik/": {}}
|
|
if !commentPreserved("k8s-mikrotik/default/traefik/http", preserve) {
|
|
t.Fatal("named rule was not preserved")
|
|
}
|
|
if commentPreserved("k8s-mikrotik/default/other/http", preserve) {
|
|
t.Fatal("unrelated rule was preserved")
|
|
}
|
|
}
|
|
|
|
type roundTripFunc func(*http.Request) (*http.Response, error)
|
|
|
|
func (function roundTripFunc) RoundTrip(request *http.Request) (*http.Response, error) {
|
|
return function(request)
|
|
}
|
|
|
|
func TestNewClientRejectsInvalidEndpointAndCA(t *testing.T) {
|
|
if _, err := NewClient("router.local", "u", "p", TLSConfig{}); err == nil {
|
|
t.Fatal("expected missing scheme to fail")
|
|
}
|
|
if _, err := NewClient("https://user:password@router.local", "u", "p", TLSConfig{}); err == nil {
|
|
t.Fatal("expected credentials in endpoint to fail")
|
|
}
|
|
if _, err := NewClient("https://router.local/rest", "u", "p", TLSConfig{}); err == nil {
|
|
t.Fatal("expected endpoint path to fail")
|
|
}
|
|
if _, err := NewClient("https://router.local", "u", "p", TLSConfig{CABundle: []byte("not PEM")}); err == nil {
|
|
t.Fatal("expected invalid CA to fail")
|
|
}
|
|
}
|