diff --git a/README.md b/README.md index 8bd9dc81..00bdb047 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,9 @@ -# Go-Akt +# Go-Akt + +[![Stability: Maintenance](https://masterminds.github.io/stability/maintenance.svg)](https://masterminds.github.io/stability/maintenance.html) + +_Go-Akt is considered feature complete and mature. +No future feature development is planned, though bugs and security issues are fixed._ [![build](https://img.shields.io/github/actions/workflow/status/Tochemey/goakt/build.yml?branch=main)](https://github.com/Tochemey/goakt/actions/workflows/build.yml) [![Go Reference](https://pkg.go.dev/badge/github.com/tochemey/goakt.svg)](https://pkg.go.dev/github.com/tochemey/goakt) diff --git a/actors/actor_test.go b/actors/actor_test.go index 6c462744..18fb6584 100644 --- a/actors/actor_test.go +++ b/actors/actor_test.go @@ -26,6 +26,7 @@ package actors import ( "context" + "fmt" "os" "sync" "testing" @@ -456,7 +457,7 @@ func startClusterSystem(t *testing.T, nodeName, serverAddr string) (ActorSystem, config := nats.Config{ ApplicationName: applicationName, ActorSystemName: actorSystemName, - NatsServer: serverAddr, + NatsServer: fmt.Sprintf("nats://%s", serverAddr), NatsSubject: natsSubject, } diff --git a/client/client_test.go b/client/client_test.go index df029d83..5610a8e8 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -639,7 +639,7 @@ func startNode(t *testing.T, logger log.Logger, nodeName, serverAddr string) (sy config := nats.Config{ ApplicationName: applicationName, ActorSystemName: actorSystemName, - NatsServer: serverAddr, + NatsServer: fmt.Sprintf("nats://%s", serverAddr), NatsSubject: natsSubject, } diff --git a/discovery/nats/config.go b/discovery/nats/config.go index b9f607bd..711602be 100644 --- a/discovery/nats/config.go +++ b/discovery/nats/config.go @@ -25,6 +25,8 @@ package nats import ( + "fmt" + "strings" "time" "github.com/tochemey/goakt/v2/internal/validation" @@ -48,8 +50,33 @@ type Config struct { func (x Config) Validate() error { return validation.New(validation.FailFast()). AddValidator(validation.NewEmptyStringValidator("NatsServer", x.NatsServer)). + AddValidator(NewServerAddrValidator(x.NatsServer)). AddValidator(validation.NewEmptyStringValidator("NatsSubject", x.NatsSubject)). AddValidator(validation.NewEmptyStringValidator("ApplicationName", x.ApplicationName)). AddValidator(validation.NewEmptyStringValidator("ActorSystemName", x.ActorSystemName)). Validate() } + +// ServerAddrValidator helps validates the NATs server address +type ServerAddrValidator struct { + server string +} + +// NewServerAddrValidator validates the nats server address +func NewServerAddrValidator(server string) validation.Validator { + return &ServerAddrValidator{server: server} +} + +// Validate execute the validation code +func (x *ServerAddrValidator) Validate() error { + // make sure that the nats prefix is set in the server address + if !strings.HasPrefix(x.server, "nats") { + return fmt.Errorf("invalid nats server address: %s", x.server) + } + + hostAndPort := strings.SplitN(x.server, "nats://", 2)[1] + return validation.NewTCPAddressValidator(hostAndPort).Validate() +} + +// enforce compilation error +var _ validation.Validator = (*ServerAddrValidator)(nil) diff --git a/discovery/nats/config_test.go b/discovery/nats/config_test.go index 0c8472d4..1f6c2dd7 100644 --- a/discovery/nats/config_test.go +++ b/discovery/nats/config_test.go @@ -40,6 +40,24 @@ func TestConfig(t *testing.T) { } assert.NoError(t, config.Validate()) }) + t.Run("With invalid nats address server", func(t *testing.T) { + config := &Config{ + NatsServer: "127.0.0.1:2322", + ApplicationName: "applicationName", + ActorSystemName: "actorSys", + NatsSubject: "nats-subject", + } + assert.Error(t, config.Validate()) + }) + t.Run("With invalid host", func(t *testing.T) { + config := &Config{ + NatsServer: "nats://:2322", + ApplicationName: "applicationName", + ActorSystemName: "actorSys", + NatsSubject: "nats-subject", + } + assert.Error(t, config.Validate()) + }) t.Run("With invalid configuration", func(t *testing.T) { config := &Config{ NatsServer: "nats://127.0.0.1:2322", diff --git a/discovery/nats/discovery_test.go b/discovery/nats/discovery_test.go index f03d0a70..f9ab1bab 100644 --- a/discovery/nats/discovery_test.go +++ b/discovery/nats/discovery_test.go @@ -25,6 +25,7 @@ package nats import ( + "fmt" "testing" "time" @@ -79,7 +80,7 @@ func newPeer(t *testing.T, serverAddr string) *Discovery { config := &Config{ ApplicationName: applicationName, ActorSystemName: actorSystemName, - NatsServer: serverAddr, + NatsServer: fmt.Sprintf("nats://%s", serverAddr), NatsSubject: natsSubject, } @@ -123,7 +124,7 @@ func TestDiscovery(t *testing.T) { config := &Config{ ApplicationName: applicationName, ActorSystemName: actorSystemName, - NatsServer: srv.Addr().String(), + NatsServer: fmt.Sprintf("nats://%s", srv.Addr().String()), NatsSubject: natsSubject, } @@ -167,7 +168,7 @@ func TestDiscovery(t *testing.T) { config := &Config{ ApplicationName: applicationName, ActorSystemName: actorSystemName, - NatsServer: srv.Addr().String(), + NatsServer: fmt.Sprintf("nats://%s", srv.Addr().String()), NatsSubject: natsSubject, } @@ -183,7 +184,6 @@ func TestDiscovery(t *testing.T) { require.NotNil(t, provider) assert.Equal(t, "nats", provider.ID()) }) - t.Run("With Initialize", func(t *testing.T) { // start the NATS server srv := startNatsServer(t) @@ -207,7 +207,7 @@ func TestDiscovery(t *testing.T) { config := &Config{ ApplicationName: applicationName, ActorSystemName: actorSystemName, - NatsServer: natsServer, + NatsServer: fmt.Sprintf("nats://%s", natsServer), NatsSubject: natsSubject, } @@ -252,7 +252,7 @@ func TestDiscovery(t *testing.T) { config := &Config{ ApplicationName: applicationName, ActorSystemName: actorSystemName, - NatsServer: natsServer, + NatsServer: fmt.Sprintf("nats://%s", natsServer), NatsSubject: natsSubject, } @@ -269,7 +269,6 @@ func TestDiscovery(t *testing.T) { provider.initialized = atomic.NewBool(true) assert.Error(t, provider.Initialize()) }) - t.Run("With Register: already registered", func(t *testing.T) { // start the NATS server srv := startNatsServer(t) @@ -293,7 +292,7 @@ func TestDiscovery(t *testing.T) { config := &Config{ ApplicationName: applicationName, ActorSystemName: actorSystemName, - NatsServer: natsServer, + NatsServer: fmt.Sprintf("nats://%s", natsServer), NatsSubject: natsSubject, } @@ -335,7 +334,7 @@ func TestDiscovery(t *testing.T) { config := &Config{ ApplicationName: applicationName, ActorSystemName: actorSystemName, - NatsServer: natsServer, + NatsServer: fmt.Sprintf("nats://%s", natsServer), NatsSubject: natsSubject, } @@ -436,7 +435,7 @@ func TestDiscovery(t *testing.T) { config := &Config{ ApplicationName: applicationName, ActorSystemName: actorSystemName, - NatsServer: natsServer, + NatsServer: fmt.Sprintf("nats://%s", natsServer), NatsSubject: natsSubject, } diff --git a/internal/cluster/engine_test.go b/internal/cluster/engine_test.go index a739bf66..85fcdf2f 100644 --- a/internal/cluster/engine_test.go +++ b/internal/cluster/engine_test.go @@ -449,7 +449,7 @@ func startEngine(t *testing.T, nodeName, serverAddr string) (*Engine, discovery. config := nats.Config{ ApplicationName: applicationName, ActorSystemName: actorSystemName, - NatsServer: serverAddr, + NatsServer: fmt.Sprintf("nats://%s", serverAddr), NatsSubject: natsSubject, } diff --git a/internal/validation/tcp_address_test.go b/internal/validation/tcp_address_test.go new file mode 100644 index 00000000..974b99c6 --- /dev/null +++ b/internal/validation/tcp_address_test.go @@ -0,0 +1,54 @@ +/* + * MIT License + * + * Copyright (c) 2022-2024 Tochemey + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package validation + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestTCPAddressValidator(t *testing.T) { + t.Run("With happy path", func(t *testing.T) { + addr := "127.0.0.1:3222" + assert.NoError(t, NewTCPAddressValidator(addr).Validate()) + }) + t.Run("With invalid port number: case 1", func(t *testing.T) { + addr := "127.0.0.1:-1" + assert.Error(t, NewTCPAddressValidator(addr).Validate()) + }) + t.Run("With invalid port number: case 2", func(t *testing.T) { + addr := "127.0.0.1:655387" + assert.Error(t, NewTCPAddressValidator(addr).Validate()) + }) + t.Run("With invalid port number: case 3", func(t *testing.T) { + addr := "127.0.0.1:0" + assert.Error(t, NewTCPAddressValidator(addr).Validate()) + }) + t.Run("With invalid host", func(t *testing.T) { + addr := ":3222" + assert.Error(t, NewTCPAddressValidator(addr).Validate()) + }) +}