Skip to content

Commit

Permalink
test: Add tests for golang and react with in memory files (#43)
Browse files Browse the repository at this point in the history
<!-- Please use this template for your pull request. -->
<!-- Please use the sections that you need and delete other sections -->

## This PR
<!-- add the description of the PR here -->

- adds tests for React and Golang

### Related Issues
#42

Fixes #42 

### How to test
`go test ./...` from root folder

---------

Signed-off-by: Florin-Mihai Anghel <[email protected]>
Signed-off-by: Florin-Mihai Anghel <[email protected]>
Co-authored-by: Michael Beemer <[email protected]>
  • Loading branch information
anghelflorinm and beeme1mr authored Oct 31, 2024
1 parent 8fa83b0 commit ce14e1c
Show file tree
Hide file tree
Showing 12 changed files with 285 additions and 8 deletions.
102 changes: 102 additions & 0 deletions cmd/generate/generate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package generate

import (
"codegen/internal/flagkeys"
"os"
"path/filepath"
"testing"

"github.com/google/go-cmp/cmp"

"github.com/spf13/afero"
"github.com/spf13/viper"
)

func TestGenerateGoSuccess(t *testing.T) {
// Constant paths.
const memoryManifestPath = "manifest/path.json"
const memoryOutputPath = "output/path.go"
const packageName = "testpackage"
const testFileManifest = "testdata/success_manifest.golden"
const testFileGo = "testdata/success_go.golden"

// Prepare in-memory files.
fs := afero.NewMemMapFs()
viper.Set(flagkeys.FileSystem, fs)
readOsFileAndWriteToMemMap(t, testFileManifest, memoryManifestPath, fs)

// Prepare command.
Root.SetArgs([]string{"go",
"--flag_manifest_path", memoryManifestPath,
"--output_path", memoryOutputPath,
"--package_name", packageName,
})

// Run command.
Root.Execute()

// Compare result.
compareOutput(t, testFileGo, memoryOutputPath, fs)
}

func TestGenerateReactSuccess(t *testing.T) {
// Constant paths.
const memoryManifestPath = "manifest/path.json"
const memoryOutputPath = "output/path.ts"
const testFileManifest = "testdata/success_manifest.golden"
const testFileReact = "testdata/success_react.golden"

// Prepare in-memory files.
fs := afero.NewMemMapFs()
viper.Set(flagkeys.FileSystem, fs)
readOsFileAndWriteToMemMap(t, testFileManifest, memoryManifestPath, fs)

// Prepare command.
Root.SetArgs([]string{"react",
"--flag_manifest_path", memoryManifestPath,
"--output_path", memoryOutputPath,
})

// Run command.
Root.Execute()

// Compare result.
compareOutput(t, testFileReact, memoryOutputPath, fs)
}

func readOsFileAndWriteToMemMap(t *testing.T, inputPath string, memPath string, memFs afero.Fs) {
data, err := os.ReadFile(inputPath)
if err != nil {
t.Fatalf("error reading file %q: %v", inputPath, err)
}
if err := memFs.MkdirAll(filepath.Dir(memPath), 0660); err != nil {
t.Fatalf("error creating directory %q: %v", filepath.Dir(memPath), err)
}
f, err := memFs.Create(memPath)
if err != nil {
t.Fatalf("error creating file %q: %v", memPath, err)
}
defer f.Close()
writtenBytes, err := f.Write(data)
if err != nil {
t.Fatalf("error writing contents to file %q: %v", memPath, err)
}
if writtenBytes != len(data) {
t.Fatalf("error writing entire file %v: writtenBytes != expectedWrittenBytes", memPath)
}
}

func compareOutput(t *testing.T, testFile, memoryOutputPath string, fs afero.Fs) {
want, err := os.ReadFile(testFile)
if err != nil {
t.Fatalf("error reading file %q: %v", testFile, err)

}
got, err := afero.ReadFile(fs, memoryOutputPath)
if err != nil {
t.Fatalf("error reading file %q: %v", memoryOutputPath, err)
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("output mismatch (-want +got):\n%s", diff)
}
}
49 changes: 49 additions & 0 deletions cmd/generate/testdata/success_go.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package testpackage

import (
"context"
"github.com/open-feature/go-sdk/openfeature"
)

type BooleanProvider func(ctx context.Context) (bool, error)
type FloatProvider func(ctx context.Context) (float64, error)
type IntProvider func(ctx context.Context) (int64, error)
type StringProvider func(ctx context.Context) (string, error)

var client *openfeature.Client = nil
// Discount percentage applied to purchases.
var DiscountPercentage = struct {
Value FloatProvider
}{
Value: func(ctx context.Context) (float64, error) {
return client.FloatValue(ctx, "discountPercentage", 0.15, openfeature.EvaluationContext{})
},
}
// Controls whether Feature A is enabled.
var EnableFeatureA = struct {
Value BooleanProvider
}{
Value: func(ctx context.Context) (bool, error) {
return client.BooleanValue(ctx, "enableFeatureA", false, openfeature.EvaluationContext{})
},
}
// The message to use for greeting users.
var GreetingMessage = struct {
Value StringProvider
}{
Value: func(ctx context.Context) (string, error) {
return client.StringValue(ctx, "greetingMessage", "Hello there!", openfeature.EvaluationContext{})
},
}
// Maximum allowed length for usernames.
var UsernameMaxLength = struct {
Value IntProvider
}{
Value: func(ctx context.Context) (int64, error) {
return client.IntValue(ctx, "usernameMaxLength", 50, openfeature.EvaluationContext{})
},
}

func init() {
client = openfeature.NewClient("testpackage")
}
32 changes: 32 additions & 0 deletions cmd/generate/testdata/success_manifest.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"flags": {
"enableFeatureA": {
"flagType": "boolean",
"defaultValue": false,
"description": "Controls whether Feature A is enabled."
},
"usernameMaxLength": {
"flagType": "integer",
"defaultValue": 50,
"description": "Maximum allowed length for usernames."
},
"greetingMessage": {
"flagType": "string",
"defaultValue": "Hello there!",
"description": "The message to use for greeting users."
},
"discountPercentage": {
"flagType": "float",
"defaultValue": 0.15,
"description": "Discount percentage applied to purchases."
},
"themeCustomization": {
"flagType": "object",
"defaultValue": {
"primaryColor": "#007bff",
"secondaryColor": "#6c757d"
},
"description": "Allows customization of theme colors."
}
}
}
55 changes: 55 additions & 0 deletions cmd/generate/testdata/success_react.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
'use client';

import {
useBooleanFlagDetails,
useNumberFlagDetails,
useStringFlagDetails,
} from "@openfeature/react-sdk";

/**
* Discount percentage applied to purchases.
*
* **Details:**
* - flag key: `discountPercentage`
* - default value: `0.15`
* - type: `number`
*/
export const useDiscountPercentage = (options: Parameters<typeof useNumberFlagDetails>[2]) => {
return useNumberFlagDetails("discountPercentage", 0.15, options);
};

/**
* Controls whether Feature A is enabled.
*
* **Details:**
* - flag key: `enableFeatureA`
* - default value: `false`
* - type: `boolean`
*/
export const useEnableFeatureA = (options: Parameters<typeof useBooleanFlagDetails>[2]) => {
return useBooleanFlagDetails("enableFeatureA", false, options);
};

/**
* The message to use for greeting users.
*
* **Details:**
* - flag key: `greetingMessage`
* - default value: `Hello there!`
* - type: `string`
*/
export const useGreetingMessage = (options: Parameters<typeof useStringFlagDetails>[2]) => {
return useStringFlagDetails("greetingMessage", "Hello there!", options);
};

/**
* Maximum allowed length for usernames.
*
* **Details:**
* - flag key: `usernameMaxLength`
* - default value: `50`
* - type: `number`
*/
export const useUsernameMaxLength = (options: Parameters<typeof useNumberFlagDetails>[2]) => {
return useNumberFlagDetails("usernameMaxLength", 50, options);
};
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ require (

require (
github.com/go-logr/logr v1.4.2 // indirect
github.com/google/go-cmp v0.6.0
github.com/spf13/cobra v1.8.1
github.com/spf13/viper v1.19.0
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI=
Expand Down
17 changes: 17 additions & 0 deletions internal/filesystem/filesystem.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Package filesystem contains the filesystem interface.
package filesystem

import (
"codegen/internal/flagkeys"

"github.com/spf13/afero"
"github.com/spf13/viper"
)

func FileSystem() afero.Fs {
return viper.Get(flagkeys.FileSystem).(afero.Fs)
}

func init() {
viper.SetDefault(flagkeys.FileSystem, afero.NewOsFs())
}
10 changes: 10 additions & 0 deletions internal/flagkeys/flagkeys.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Package commonflags contains keys for all command-line flags related to openfeature CLI.
package flagkeys

import "github.com/spf13/viper"

const (
// `generate` flags:
// FlagManifestPath is the key for the flag that stores the flag manifest path.
Expand All @@ -11,4 +13,12 @@ const (
// `generate go` flags:
// GoPackageName is the key for the flag that stores the Golang package name.
GoPackageName = "package_name"

//internal keys:
// FileSystem is the key for the flag that stores the filesystem interface.
FileSystem = "filesystem"
)

func init() {
viper.SetDefault(FileSystem, "local")
}
7 changes: 4 additions & 3 deletions internal/generate/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ package generate

import (
"bytes"
"codegen/internal/filesystem"
"codegen/internal/flagkeys"
"codegen/internal/generate/manifestutils"
"codegen/internal/generate/types"
"fmt"
"os"
"path"
"path/filepath"
"text/template"
Expand All @@ -28,10 +28,11 @@ func GenerateFile(funcs template.FuncMap, contents string, data types.TmplDataIn
return fmt.Errorf("error executing template: %v", err)
}
outputPath := data.BaseTmplDataInfo().OutputPath
if err := os.MkdirAll(filepath.Dir(outputPath), 0770); err != nil {
fs := filesystem.FileSystem()
if err := fs.MkdirAll(filepath.Dir(outputPath), 0660); err != nil {
return err
}
f, err := os.Create(path.Join(outputPath))
f, err := fs.Create(path.Join(outputPath))
if err != nil {
return fmt.Errorf("error creating file %q: %v", outputPath, err)
}
Expand Down
11 changes: 9 additions & 2 deletions internal/generate/manifestutils/manifestutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,23 @@ package manifestutils

import (
flagmanifest "codegen/docs/schema/v0"
"codegen/internal/filesystem"
"codegen/internal/flagkeys"
"codegen/internal/generate/types"
"encoding/json"
"fmt"
"os"
"sort"
"strconv"

"github.com/santhosh-tekuri/jsonschema/v5"
"github.com/spf13/afero"
"github.com/spf13/viper"
)

// LoadData loads the data from the flag manifest.
func LoadData(manifestPath string, supportedFlagTypes map[types.FlagType]bool) (*types.BaseTmplData, error) {
data, err := os.ReadFile(manifestPath)
fs := filesystem.FileSystem()
data, err := afero.ReadFile(fs, manifestPath)
if err != nil {
return nil, fmt.Errorf("error reading contents from file %q", manifestPath)
}
Expand Down Expand Up @@ -101,5 +104,9 @@ func unmarshalFlagManifest(data []byte) (*types.BaseTmplData, error) {
Docs: docs,
})
}
// Ensure consistency of order of flag generation.
sort.Slice(btData.Flags, func(i, j int) bool {
return btData.Flags[i].Name < btData.Flags[j].Name
})
return &btData, nil
}
1 change: 1 addition & 0 deletions internal/testutils/testutils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package testutils
6 changes: 3 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import "codegen/cmd"

var (
// Overridden by Go Releaser at build time
version = "dev"
commit = "HEAD"
date = "unknown"
version = "dev"
commit = "HEAD"
date = "unknown"
)

func main() {
Expand Down

0 comments on commit ce14e1c

Please sign in to comment.