Skip to content

Commit

Permalink
chore: refactoring / tests (#723)
Browse files Browse the repository at this point in the history
  • Loading branch information
mattjohnsonpint authored Jan 24, 2025
1 parent eab9221 commit 8d8f7e2
Show file tree
Hide file tree
Showing 40 changed files with 1,048 additions and 478 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
- feat: support type aliases and redefinitions
[#721](https://github.com/hypermodeinc/modus/pull/721)
- feat: support MySQL database connections [#722](https://github.com/hypermodeinc/modus/pull/722)
- chore: refactoring / tests [#723](https://github.com/hypermodeinc/modus/pull/723)

## 2025-01-09 - CLI 0.16.6

Expand Down
51 changes: 46 additions & 5 deletions runtime/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,34 +15,75 @@ import (
"path/filepath"
"runtime"
"sync"
"time"
)

// ShutdownTimeout is the time to wait for the server to shutdown gracefully.
const ShutdownTimeout = 5 * time.Second
"github.com/fatih/color"
)

var mu = &sync.RWMutex{}
var config *AppConfig
var shuttingDown = false

func init() {
// Set the global color mode
SetOutputColorMode()

// Create the the app configuration
mu.Lock()
defer mu.Unlock()
config = CreateAppConfig()
}

// SetOutputColorMode applies the FORCE_COLOR environment variable to override the default color mode.
func SetOutputColorMode() {
forceColor := os.Getenv("FORCE_COLOR")
if forceColor != "" && forceColor != "0" {
color.NoColor = false
}
}

// Config returns the global app configuration.
func Config() *AppConfig {
mu.RLock()
defer mu.RUnlock()
return config
}

// SetConfig sets the global app configuration.
// This is typically only called in tests.
func SetConfig(c *AppConfig) {
mu.Lock()
defer mu.Unlock()
config = c
}

// IsDevEnvironment returns true if the application is running in a development environment.
func IsDevEnvironment() bool {
return Config().IsDevEnvironment()
}

// IsShuttingDown returns true if the application is in the process of a graceful shutdown.
func IsShuttingDown() bool {
mu.RLock()
defer mu.RUnlock()
return shuttingDown
}

// SetShuttingDown sets the application to a shutting down state during a graceful shutdown.
func SetShuttingDown() {
mu.Lock()
defer mu.Unlock()
shuttingDown = true
}

// GetRootSourcePath returns the root path of the source code.
// It is used to trim the paths in stack traces when included in telemetry.
func GetRootSourcePath() string {
_, filename, _, ok := runtime.Caller(0)
if !ok {
return ""
}

return path.Join(path.Dir(filename), "../") + "/"
return path.Dir(path.Dir(filename)) + "/"
}

func ModusHomeDir() string {
Expand Down
67 changes: 67 additions & 0 deletions runtime/app/app_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright 2025 Hypermode Inc.
* Licensed under the terms of the Apache License, Version 2.0
* See the LICENSE file that accompanied this code for further details.
*
* SPDX-FileCopyrightText: 2025 Hypermode Inc. <[email protected]>
* SPDX-License-Identifier: Apache-2.0
*/

package app_test

import (
"os"
"path"
"testing"

"github.com/fatih/color"
"github.com/hypermodeinc/modus/runtime/app"
)

func TestGetRootSourcePath(t *testing.T) {
cwd, _ := os.Getwd()
expectedPath := path.Dir(cwd) + "/"
actualPath := app.GetRootSourcePath()

if actualPath != expectedPath {
t.Errorf("Expected path: %s, but got: %s", expectedPath, actualPath)
}
}
func TestIsShuttingDown(t *testing.T) {
if app.IsShuttingDown() {
t.Errorf("Expected initial state to be not shutting down")
}

app.SetShuttingDown()

if !app.IsShuttingDown() {
t.Errorf("Expected state to be shutting down")
}
}

func TestSetConfig(t *testing.T) {
initialConfig := app.Config()
if initialConfig == nil {
t.Errorf("Expected initial config to be non-nil")
}

newConfig := &app.AppConfig{}
app.SetConfig(newConfig)

if app.Config() != newConfig {
t.Errorf("Expected config to be updated")
}
}

func TestForceColor(t *testing.T) {
if !color.NoColor {
t.Errorf("Expected NoColor to be true")
}

os.Setenv("FORCE_COLOR", "1")
app.SetOutputColorMode()

if color.NoColor {
t.Errorf("Expected NoColor to be false")
}
}
159 changes: 159 additions & 0 deletions runtime/app/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
/*
* Copyright 2025 Hypermode Inc.
* Licensed under the terms of the Apache License, Version 2.0
* See the LICENSE file that accompanied this code for further details.
*
* SPDX-FileCopyrightText: 2025 Hypermode Inc. <[email protected]>
* SPDX-License-Identifier: Apache-2.0
*/

package app

import (
"flag"
"fmt"
"os"
"strings"
"time"
)

type AppConfig struct {
environment string
port int
appPath string
useAwsStorage bool
s3Bucket string
s3Path string
refreshInterval time.Duration
useJsonLogging bool
}

func (c *AppConfig) Environment() string {
return c.environment
}

func (c *AppConfig) Port() int {
return c.port
}

func (c *AppConfig) AppPath() string {
return c.appPath
}

func (c *AppConfig) UseAwsStorage() bool {
return c.useAwsStorage
}

func (c *AppConfig) S3Bucket() string {
return c.s3Bucket
}

func (c *AppConfig) S3Path() string {
return c.s3Path
}

func (c *AppConfig) RefreshInterval() time.Duration {
return c.refreshInterval
}

func (c *AppConfig) UseJsonLogging() bool {
return c.useJsonLogging
}

func (c *AppConfig) IsDevEnvironment() bool {
// support either name (but prefer "dev")
return c.environment == "dev" || c.environment == "development"
}

func (c *AppConfig) WithEnvironment(environment string) *AppConfig {
cfg := *c
cfg.environment = environment
return &cfg
}

func (c *AppConfig) WithPort(port int) *AppConfig {
cfg := *c
cfg.port = port
return &cfg
}

func (c *AppConfig) WithAppPath(appPath string) *AppConfig {
cfg := *c
cfg.appPath = appPath
return &cfg
}

func (c *AppConfig) WithS3Storage(s3Bucket, s3Path string) *AppConfig {
cfg := *c
cfg.useAwsStorage = true
cfg.s3Bucket = s3Bucket
cfg.s3Path = s3Path
return &cfg
}

func (c *AppConfig) WithRefreshInterval(interval time.Duration) *AppConfig {
cfg := *c
cfg.refreshInterval = interval
return &cfg
}

func (c *AppConfig) WithJsonLogging() *AppConfig {
cfg := *c
cfg.useJsonLogging = true
return &cfg
}

// Creates a new AppConfig instance with default values.
func NewAppConfig() *AppConfig {
return &AppConfig{
port: 8686,
environment: "prod",
refreshInterval: time.Second * 5,
}
}

// Creates the app configuration from the command line flags and environment variables.
func CreateAppConfig() *AppConfig {

cfg := NewAppConfig()

fs := flag.NewFlagSet("", flag.ContinueOnError)

fs.StringVar(&cfg.appPath, "appPath", cfg.appPath, "REQUIRED - The path to the Modus app to load and run.")
fs.IntVar(&cfg.port, "port", cfg.port, "The HTTP port to listen on.")

fs.BoolVar(&cfg.useAwsStorage, "useAwsStorage", cfg.useAwsStorage, "Use AWS S3 for storage instead of the local filesystem.")
fs.StringVar(&cfg.s3Bucket, "s3bucket", cfg.s3Bucket, "The S3 bucket to use, if using AWS storage.")
fs.StringVar(&cfg.s3Path, "s3path", cfg.s3Path, "The path within the S3 bucket to use, if using AWS storage.")

fs.DurationVar(&cfg.refreshInterval, "refresh", cfg.refreshInterval, "The refresh interval to reload any changes.")
fs.BoolVar(&cfg.useJsonLogging, "jsonlogs", cfg.useJsonLogging, "Use JSON format for logging.")

var showVersion bool
const versionUsage = "Show the Runtime version number and exit."
fs.BoolVar(&showVersion, "version", false, versionUsage)
fs.BoolVar(&showVersion, "v", false, versionUsage+" (shorthand)")

args := make([]string, 0, len(os.Args))
for i := 1; i < len(os.Args); i++ {
if !strings.HasPrefix(os.Args[i], "-test.") {
args = append(args, os.Args[i])
}
}

if err := fs.Parse(args); err != nil {
fmt.Fprintf(os.Stderr, "Error parsing command line flags: %v\n", err)
os.Exit(1)
}

if showVersion {
fmt.Println(ProductVersion())
os.Exit(0)
}

if env := os.Getenv("MODUS_ENV"); env != "" {
cfg.environment = env
}

return cfg
}
Loading

0 comments on commit 8d8f7e2

Please sign in to comment.