Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: refactoring / tests #723

Merged
merged 6 commits into from
Jan 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading