Skip to content

Commit

Permalink
(ref) #26: adds more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
benammann committed Sep 11, 2022
1 parent c1e9bb5 commit 8e57bcb
Show file tree
Hide file tree
Showing 14 changed files with 244 additions and 21 deletions.
2 changes: 1 addition & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,6 @@ func resolveContext() {

func createRenderingEngine() {
if projectCfg != nil {
renderingEngine = render.NewRenderingEngine(projectCfg)
renderingEngine = render.NewRenderingEngine(projectCfg, fs, fs)
}
}
6 changes: 3 additions & 3 deletions docs/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,12 @@ DATABASE_PASSWORD={{.Secrets.databasePassword}}
You can have custom renderTargets to render files. For example `env` or `k8s`. You can than add multiple files to a renderTargets.

````bash
# always render .env.dist to .env
# always render empty.dist to .env
# uses the targetName: env
git secrets add file .env.dist .env -t env
git secrets add file empty.dist .env -t env

# now execute the rendering process
# this renders the .env.dist file to .env and fills out all variables using the default context
# this renders the empty.dist file to .env and fills out all variables using the default context
# targetName: env
git secrets render env

Expand Down
1 change: 1 addition & 0 deletions pkg/config/const/const_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package config_const
File renamed without changes.
4 changes: 2 additions & 2 deletions pkg/config/generic/repository_secret.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,8 @@ func (s *Secret) Decode() (string, error) {
return s.OriginContext.DecodeValue(s.EncodedValue)
}

// DecodeSecrets decodes the secrets of the current context and puts them into a map[string]string
func (c *Repository) DecodeSecrets() (SecretsMap, error) {
// GetSecretsMapDecoded decodes the secrets of the current context and puts them into a map[string]string
func (c *Repository) GetSecretsMapDecoded() (SecretsMap, error) {

// create the secrets map
secretsMap := make(SecretsMap)
Expand Down
File renamed without changes.
17 changes: 11 additions & 6 deletions pkg/render/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"fmt"
config_generic "github.com/benammann/git-secrets/pkg/config/generic"
"github.com/spf13/afero"
"html/template"
"io"
"os"
Expand All @@ -12,6 +13,8 @@ import (

type RenderingEngine struct {
repository *config_generic.Repository
fsIn afero.Fs
fsOut afero.Fs
}

type RenderingContext struct {
Expand All @@ -21,21 +24,23 @@ type RenderingContext struct {
Configs config_generic.ConfigMap
}

func NewRenderingEngine(repository *config_generic.Repository) *RenderingEngine {
func NewRenderingEngine(repository *config_generic.Repository, fsIn afero.Fs, fsOut afero.Fs) *RenderingEngine {
return &RenderingEngine{
repository: repository,
fsIn: fsIn,
fsOut: fsOut,
}
}

// createTemplate creates a new template
func (e *RenderingEngine) createTemplate(fileIn string) (*template.Template, error) {
return createNewTemplate(fileIn)
func (e *RenderingEngine) createTemplate(fileIn string) (*template.Template, error) {
return createTemplate(e.fsIn, fileIn)
}

// CreateRenderingContext creates the context which is used in the templates
func (e *RenderingEngine) CreateRenderingContext(fileToRender *config_generic.FileToRender) (*RenderingContext, error) {

// decode the secrets
secretsMap, errSecrets := e.repository.DecodeSecrets()
secretsMap, errSecrets := e.repository.GetSecretsMapDecoded()
if errSecrets != nil {
return nil, fmt.Errorf("could not create context secrets: %s", errSecrets.Error())
}
Expand Down Expand Up @@ -69,7 +74,7 @@ func (e *RenderingEngine) RenderFile(fileToRender *config_generic.FileToRender)
func (e *RenderingEngine) WriteFile(fileToRender *config_generic.FileToRender) (usedContext *RenderingContext, err error) {

// open the file
fsFileOut, errFsFile := os.OpenFile(fileToRender.FileOut, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
fsFileOut, errFsFile := e.fsOut.OpenFile(fileToRender.FileOut, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
if fsFileOut != nil {
defer fsFileOut.Close()
}
Expand Down
24 changes: 18 additions & 6 deletions pkg/render/render_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,20 @@ package render

import (
"encoding/base64"
"github.com/spf13/afero"
"github.com/tcnksm/go-gitconfig"
"html/template"
"log"
"io/fs"
)

type AferoConvFs struct {
aferoFs afero.Fs
}

func (ac AferoConvFs) Open(name string) (fs.File, error) {
return ac.aferoFs.Open(name)
}

// getTemplateFunctions are added to the template and can be executed
func getTemplateFunctions() template.FuncMap {
return template.FuncMap{
Expand All @@ -27,23 +36,26 @@ func templateFunctionGitConfig(args ...interface{}) interface{} {
if err != nil {
globalVal, errGlobal := gitconfig.Global(args[0].(string))
if errGlobal != nil {
log.Fatalf("the key %s does not exist locally or globally", args[0].(string))
return ""
}
return globalVal
}
return val
}

// createNewTemplate creates a new template engine with all the extensions based on the file name
func createNewTemplate(pathToFile string) (*template.Template, error) {
func createTemplate(fs afero.Fs, pathToFile string) (*template.Template, error) {

// create the new engine with file base name
tpl := template.New("")

// add the template functions
tpl.Funcs(getTemplateFunctions())

tpl, err := tpl.ParseFiles(pathToFile)
tpl, err := tpl.ParseFS(AferoConvFs{aferoFs: fs}, pathToFile)

if err != nil {
return nil, err
}

return tpl, err
}
}
155 changes: 155 additions & 0 deletions pkg/render/render_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package render

import (
"bytes"
"embed"
"encoding/json"
"fmt"
config_generic "github.com/benammann/git-secrets/pkg/config/generic"
global_config "github.com/benammann/git-secrets/pkg/config/global"
"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
"testing"
)

const GlobalSecretKey = "gitSecretsTest"
const GlobalSecretValue = "eeSaoghoh8oi9leed7hai4looK3jae1N"

const FileRenderTestDefault = "render-test-default.json"

//go:embed test_fs
var testFiles embed.FS
var testFileAfero = &afero.FromIOFS{
FS: testFiles,
}

type TestRenderFunctions struct {
Base64Encode string `json:"base64Encode"`
}

type TestRenderContext struct {
RenderContext *RenderingContext `json:"context"`
TestFunctions *TestRenderFunctions `json:"functions"`
}

func createTestRepository(fileName string, selectedContextName string) (*config_generic.Repository, error) {
fileName = fmt.Sprintf("test_fs/%s", fileName)
globalConfig := global_config.NewGlobalConfigProvider(global_config.NewMemoryStorageProvider())
_ = globalConfig.SetSecret(GlobalSecretKey, GlobalSecretValue, false)
mergeGlobalSecrets := make(map[string]string)
repository, errParse := config_generic.ParseRepository(afero.FromIOFS{
FS: testFiles,
}, fileName, globalConfig, mergeGlobalSecrets)
if errParse != nil {
return nil, fmt.Errorf("could not parse: %s", errParse.Error())
}
_, errSetContext := repository.SetSelectedContext(selectedContextName)
if errSetContext != nil {
return nil, fmt.Errorf("could not set context: %s", errSetContext.Error())
}
return repository, nil
}

func initRepository(t *testing.T, fileName string, selectedContextName string) (*config_generic.Repository, *RenderingEngine) {
repo, errParse := createTestRepository(fileName, selectedContextName)
assert.NotNil(t, repo)
assert.NoError(t, errParse)
return repo, NewRenderingEngine(repo, testFileAfero, testFileAfero)
}

func TestNewRenderingEngine(t *testing.T) {
_, engine := initRepository(t, FileRenderTestDefault, "default")
assert.NotNil(t, engine)
}

func TestRenderingEngine_CreateRenderingContext(t *testing.T) {
repo, engine := initRepository(t, FileRenderTestDefault, "default")
assert.NotNil(t, engine)

file := &config_generic.FileToRender{
FileIn: "fileIn",
FileOut: "fileOut",
}

dbPassword := repo.GetCurrentSecret("databasePassword")
dbPasswordVal, errDecode := dbPassword.Decode()
assert.NoError(t, errDecode)

ctx, err := engine.CreateRenderingContext(file)
assert.NoError(t, err)
assert.Equal(t, "default", ctx.ContextName)
assert.Equal(t, dbPasswordVal, ctx.Secrets["databasePassword"])
assert.Equal(t, "3306", ctx.Configs["databasePort"])
}

func TestRenderingEngine_ExecuteTemplate(t *testing.T) {
_, engine := initRepository(t, FileRenderTestDefault, "default")
fileToRender := &config_generic.FileToRender{
FileIn: "test_fs/templates/render-context.json",
}

var bytesOut bytes.Buffer
usedContext, errExecute := engine.ExecuteTemplate(fileToRender, &bytesOut)
assert.NoError(t, errExecute)
assert.NotNil(t, usedContext)

var renderedFileDecoded TestRenderContext
errDecode := json.Unmarshal(bytesOut.Bytes(), &renderedFileDecoded)
assert.NoError(t, errDecode)
assert.NotNil(t, renderedFileDecoded.RenderContext)

renderContext := renderedFileDecoded.RenderContext
assert.Equal(t, "default", usedContext.ContextName)
assert.Equal(t, usedContext.ContextName, renderContext.ContextName)
assert.Equal(t, usedContext.File.FileIn, renderContext.File.FileIn)
assert.Equal(t, usedContext.File.FileOut, renderContext.File.FileOut)
assert.Equal(t, "3306", usedContext.Configs["databasePort"])
assert.Equal(t, usedContext.Secrets["databasePort"], renderContext.Secrets["databasePort"])
assert.Equal(t, "em8toheGhieh0Thu1ahz9Lou2ucheeh6", usedContext.Secrets["databasePassword"])
assert.Equal(t, usedContext.Secrets["databasePassword"], renderContext.Secrets["databasePassword"])

}

func TestRenderingEngine_RenderFile(t *testing.T) {
_, engine := initRepository(t, FileRenderTestDefault, "default")
fileToRender := &config_generic.FileToRender{
FileIn: "test_fs/templates/render-context.json",
}
_, fileContents, err := engine.RenderFile(fileToRender)
assert.NoError(t, err)
assert.NotEqual(t, "", fileContents)
}

func TestRenderingEngine_WriteFile(t *testing.T) {
_, engine := initRepository(t, FileRenderTestDefault, "default")
fileToRender := &config_generic.FileToRender{
FileIn: "test_fs/templates/render-context.json",
FileOut: "render-context.json",
}
engine.fsOut = afero.NewMemMapFs()
_, err := engine.WriteFile(fileToRender)
assert.NoError(t, err)

fileExists, errExists := afero.Exists(engine.fsOut, fileToRender.FileOut)
assert.NoError(t, errExists)
assert.True(t, fileExists)

}

func TestRenderingEngine_createTemplate(t *testing.T) {
fs := afero.FromIOFS{FS: testFiles}

t.Run("create template for existing file", func(t *testing.T) {
tpl, err := createTemplate(fs, "test_fs/templates/render-context.json")
assert.NoError(t, err)
assert.NotNil(t, tpl)
})

t.Run("fail if file not exists", func(t *testing.T) {
tpl, err := createTemplate(fs, "test_fs/templates/missing-file")
assert.Error(t, err)
assert.Nil(t, tpl)
})


}
2 changes: 2 additions & 0 deletions pkg/render/test_fs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
out/*
!out/.gitkeep
Empty file added pkg/render/test_fs/out/.gitkeep
Empty file.
28 changes: 28 additions & 0 deletions pkg/render/test_fs/render-test-default.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"$schema": "https://raw.githubusercontent.com/benammann/git-secrets/dev-beta/schema/def/v1.json",
"version": 1,
"context": {
"default": {
"decryptSecret": {
"fromName": "gitsecretstest"
},
"secrets": {
"databasePassword": "NkUBz4cXXAHkz23ygUZzgR0K+sBTzhPqtws+Jib1s9WXzCl8Iz05CI4ywaBHWJh3XzLnI8c9eG4qauIc"
},
"configs": {
"databasePort": "3306"
}
},
"prod": {}
},
"renderFiles": {
"test-render-all": {
"files": [
{
"fileIn": "templates/render-context.json",
"fileOut": "out/render-context.json"
}
]
}
}
}
20 changes: 20 additions & 0 deletions pkg/render/test_fs/templates/render-context.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{{$hello := "Hello World"}}{
"context": {
"ContextName": "{{.ContextName}}",
"File": {
"FileIn": "{{.File.FileIn}}",
"FileOut": "{{.File.FileOut}}"
},
"Secrets": {
"databasePassword": "{{.Secrets.databasePassword}}"
},
"Configs": {
"databasePort": "{{.Configs.databasePort}}"
}
},
"functions": {
"base64Encode": "{{Base64Encode $hello}}",
"gitConfig": "{{GitConfig "user.name"}}"
}
}

6 changes: 3 additions & 3 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,12 +157,12 @@ DATABASE_PASSWORD={{.Secrets.databasePassword}}
You can have custom renderTargets to render files. For example `env` or `k8s`. You can than add multiple files to a renderTargets.

````bash
# always render .env.dist to .env
# always render empty.dist to .env
# uses the targetName: env
git secrets add file .env.dist .env -t env
git secrets add file empty.dist .env -t env

# now execute the rendering process
# this renders the .env.dist file to .env and fills out all variables using the default context
# this renders the empty.dist file to .env and fills out all variables using the default context
# targetName: env
git secrets render env

Expand Down

0 comments on commit 8e57bcb

Please sign in to comment.