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

test: Add tests for golang and react with in memory files #43

Merged
merged 24 commits into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
c67f767
feat!: initial commit for codegen CLI with golang support
anghelflorinm Sep 10, 2024
ec78985
docs!: add comments to few missing exported functions
anghelflorinm Sep 10, 2024
32f8069
refactor!: add better error messages for the CLI
anghelflorinm Sep 10, 2024
e4b02d0
refactor: move package to the top of the repository
anghelflorinm Sep 24, 2024
47889fc
refactor: move main file to top directory
anghelflorinm Sep 24, 2024
e5620ed
refactor: embed flag manifest schema into the script
anghelflorinm Sep 26, 2024
faf47d4
Merge branch 'main' into 2-cli-prototype
anghelflorinm Sep 26, 2024
9b8ddd8
fix: remove files after being moved
anghelflorinm Sep 26, 2024
5bdfc10
refactor: move main.go back into SRC and attach module to flagmanifes…
anghelflorinm Sep 30, 2024
896ac8d
Merge remote-tracking branch 'origin' into 2-cli-prototype
anghelflorinm Oct 1, 2024
a5059d0
refactor: change property names in manifest to camel case format
anghelflorinm Oct 1, 2024
01240cc
refactor: change property names to camel case
anghelflorinm Oct 1, 2024
301a51b
Merge remote-tracking branch 'origin' into 2-cli-prototype
anghelflorinm Oct 2, 2024
9c37b8f
Merge remote-tracking branch 'origin' into 2-cli-prototype
anghelflorinm Oct 3, 2024
96b2139
Merge remote-tracking branch 'origin' into 2-cli-prototype
anghelflorinm Oct 7, 2024
c766e83
refactor: change folder, package structure; integrate with cobra
anghelflorinm Oct 9, 2024
087b76b
chore: delete comment; this has been replaced with Git project issue
anghelflorinm Oct 10, 2024
36124c8
chore: update internal/generate/plugins/golang/golang.tmpl
anghelflorinm Oct 10, 2024
d858654
Merge remote-tracking branch 'origin' into 2-cli-prototype
anghelflorinm Oct 23, 2024
97cdaff
test: add small tests for generate command for go and react with in m…
anghelflorinm Oct 29, 2024
4682490
Merge branch '2-cli-prototype' of https://github.com/open-feature/cod…
anghelflorinm Oct 29, 2024
09f0ac5
chore: update cmd/generate/generate_test.go
anghelflorinm Oct 30, 2024
6509bda
chore: update cmd/generate/generate_test.go
anghelflorinm Oct 30, 2024
efdcc4b
chore: update the permissions when making directory
anghelflorinm Oct 31, 2024
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
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 TestGenerateGoSucces(t *testing.T) {
anghelflorinm marked this conversation as resolved.
Show resolved Hide resolved
// 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 TestGenerateReactSucces(t *testing.T) {
anghelflorinm marked this conversation as resolved.
Show resolved Hide resolved
// 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), 0770); 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), 0770); err != nil {
anghelflorinm marked this conversation as resolved.
Show resolved Hide resolved
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
Loading