safing-portmaster/base/config/get_test.go
Daniel Hååvi 80664d1a27
Restructure modules ()
* Move portbase into monorepo

* Add new simple module mgr

* [WIP] Switch to new simple module mgr

* Add StateMgr and more worker variants

* [WIP] Switch more modules

* [WIP] Switch more modules

* [WIP] swtich more modules

* [WIP] switch all SPN modules

* [WIP] switch all service modules

* [WIP] Convert all workers to the new module system

* [WIP] add new task system to module manager

* [WIP] Add second take for scheduling workers

* [WIP] Add FIXME for bugs in new scheduler

* [WIP] Add minor improvements to scheduler

* [WIP] Add new worker scheduler

* [WIP] Fix more bug related to new module system

* [WIP] Fix start handing of the new module system

* [WIP] Improve startup process

* [WIP] Fix minor issues

* [WIP] Fix missing subsystem in settings

* [WIP] Initialize managers in constructor

* [WIP] Move module event initialization to constrictors

* [WIP] Fix setting for enabling and disabling the SPN module

* [WIP] Move API registeration into module construction

* [WIP] Update states mgr for all modules

* [WIP] Add CmdLine operation support

* Add state helper methods to module group and instance

* Add notification and module status handling to status package

* Fix starting issues

* Remove pilot widget and update security lock to new status data

* Remove debug logs

* Improve http server shutdown

* Add workaround for cleanly shutting down firewall+netquery

* Improve logging

* Add syncing states with notifications for new module system

* Improve starting, stopping, shutdown; resolve FIXMEs/TODOs

* [WIP] Fix most unit tests

* Review new module system and fix minor issues

* Push shutdown and restart events again via API

* Set sleep mode via interface

* Update example/template module

* [WIP] Fix spn/cabin unit test

* Remove deprecated UI elements

* Make log output more similar for the logging transition phase

* Switch spn hub and observer cmds to new module system

* Fix log sources

* Make worker mgr less error prone

* Fix tests and minor issues

* Fix observation hub

* Improve shutdown and restart handling

* Split up big connection.go source file

* Move varint and dsd packages to structures repo

* Improve expansion test

* Fix linter warnings

* Fix interception module on windows

* Fix linter errors

---------

Co-authored-by: Vladimir Stoilov <vladimir@safing.io>
2024-08-09 18:15:48 +03:00

368 lines
7.4 KiB
Go

package config
import (
"encoding/json"
"fmt"
"testing"
"github.com/safing/portmaster/base/log"
)
func parseAndReplaceConfig(jsonData string) error {
m, err := JSONToMap([]byte(jsonData))
if err != nil {
return err
}
validationErrors, _ := ReplaceConfig(m)
if len(validationErrors) > 0 {
return fmt.Errorf("%d errors, first: %w", len(validationErrors), validationErrors[0])
}
return nil
}
func parseAndReplaceDefaultConfig(jsonData string) error {
m, err := JSONToMap([]byte(jsonData))
if err != nil {
return err
}
validationErrors, _ := ReplaceDefaultConfig(m)
if len(validationErrors) > 0 {
return fmt.Errorf("%d errors, first: %w", len(validationErrors), validationErrors[0])
}
return nil
}
func quickRegister(t *testing.T, key string, optType OptionType, defaultValue interface{}) {
t.Helper()
err := Register(&Option{
Name: key,
Key: key,
Description: "test config",
ReleaseLevel: ReleaseLevelStable,
ExpertiseLevel: ExpertiseLevelUser,
OptType: optType,
DefaultValue: defaultValue,
})
if err != nil {
t.Fatal(err)
}
}
func TestGet(t *testing.T) { //nolint:paralleltest
// reset
options = make(map[string]*Option)
err := log.Start()
if err != nil {
t.Fatal(err)
}
quickRegister(t, "monkey", OptTypeString, "c")
quickRegister(t, "zebras/zebra", OptTypeStringArray, []string{"a", "b"})
quickRegister(t, "elephant", OptTypeInt, -1)
quickRegister(t, "hot", OptTypeBool, false)
quickRegister(t, "cold", OptTypeBool, true)
err = parseAndReplaceConfig(`
{
"monkey": "a",
"zebras": {
"zebra": ["black", "white"]
},
"elephant": 2,
"hot": true,
"cold": false
}
`)
if err != nil {
t.Fatal(err)
}
err = parseAndReplaceDefaultConfig(`
{
"monkey": "b",
"snake": "0",
"elephant": 0
}
`)
if err != nil {
t.Fatal(err)
}
monkey := GetAsString("monkey", "none")
if monkey() != "a" {
t.Errorf("monkey should be a, is %s", monkey())
}
zebra := GetAsStringArray("zebras/zebra", []string{})
if len(zebra()) != 2 || zebra()[0] != "black" || zebra()[1] != "white" {
t.Errorf("zebra should be [\"black\", \"white\"], is %v", zebra())
}
elephant := GetAsInt("elephant", -1)
if elephant() != 2 {
t.Errorf("elephant should be 2, is %d", elephant())
}
hot := GetAsBool("hot", false)
if !hot() {
t.Errorf("hot should be true, is %v", hot())
}
cold := GetAsBool("cold", true)
if cold() {
t.Errorf("cold should be false, is %v", cold())
}
err = parseAndReplaceConfig(`
{
"monkey": "3"
}
`)
if err != nil {
t.Fatal(err)
}
if monkey() != "3" {
t.Errorf("monkey should be 0, is %s", monkey())
}
if elephant() != 0 {
t.Errorf("elephant should be 0, is %d", elephant())
}
zebra()
hot()
// concurrent
GetAsString("monkey", "none")()
GetAsStringArray("zebras/zebra", []string{})()
GetAsInt("elephant", -1)()
GetAsBool("hot", false)()
// perspective
// load data
pLoaded := make(map[string]interface{})
err = json.Unmarshal([]byte(`{
"monkey": "a",
"zebras": {
"zebra": ["black", "white"]
},
"elephant": 2,
"hot": true,
"cold": false
}`), &pLoaded)
if err != nil {
t.Fatal(err)
}
// create
p, err := NewPerspective(pLoaded)
if err != nil {
t.Fatal(err)
}
monkeyVal, ok := p.GetAsString("monkey")
if !ok || monkeyVal != "a" {
t.Errorf("[perspective] monkey should be a, is %+v", monkeyVal)
}
zebraVal, ok := p.GetAsStringArray("zebras/zebra")
if !ok || len(zebraVal) != 2 || zebraVal[0] != "black" || zebraVal[1] != "white" {
t.Errorf("[perspective] zebra should be [\"black\", \"white\"], is %+v", zebraVal)
}
elephantVal, ok := p.GetAsInt("elephant")
if !ok || elephantVal != 2 {
t.Errorf("[perspective] elephant should be 2, is %+v", elephantVal)
}
hotVal, ok := p.GetAsBool("hot")
if !ok || !hotVal {
t.Errorf("[perspective] hot should be true, is %+v", hotVal)
}
coldVal, ok := p.GetAsBool("cold")
if !ok || coldVal {
t.Errorf("[perspective] cold should be false, is %+v", coldVal)
}
}
func TestReleaseLevel(t *testing.T) { //nolint:paralleltest
// reset
options = make(map[string]*Option)
registerReleaseLevelOption()
// setup
subsystemOption := &Option{
Name: "test subsystem",
Key: "subsystem/test",
Description: "test config",
ReleaseLevel: ReleaseLevelStable,
ExpertiseLevel: ExpertiseLevelUser,
OptType: OptTypeBool,
DefaultValue: false,
}
err := Register(subsystemOption)
if err != nil {
t.Fatal(err)
}
err = SetConfigOption("subsystem/test", true)
if err != nil {
t.Fatal(err)
}
testSubsystem := GetAsBool("subsystem/test", false)
// test option level stable
subsystemOption.ReleaseLevel = ReleaseLevelStable
err = SetConfigOption(releaseLevelKey, ReleaseLevelNameStable)
if err != nil {
t.Fatal(err)
}
if !testSubsystem() {
t.Error("should be active")
}
err = SetConfigOption(releaseLevelKey, ReleaseLevelNameBeta)
if err != nil {
t.Fatal(err)
}
if !testSubsystem() {
t.Error("should be active")
}
err = SetConfigOption(releaseLevelKey, ReleaseLevelNameExperimental)
if err != nil {
t.Fatal(err)
}
if !testSubsystem() {
t.Error("should be active")
}
// test option level beta
subsystemOption.ReleaseLevel = ReleaseLevelBeta
err = SetConfigOption(releaseLevelKey, ReleaseLevelNameStable)
if err != nil {
t.Fatal(err)
}
if testSubsystem() {
t.Errorf("should be inactive: opt=%d system=%d", subsystemOption.ReleaseLevel, getReleaseLevel())
}
err = SetConfigOption(releaseLevelKey, ReleaseLevelNameBeta)
if err != nil {
t.Fatal(err)
}
if !testSubsystem() {
t.Error("should be active")
}
err = SetConfigOption(releaseLevelKey, ReleaseLevelNameExperimental)
if err != nil {
t.Fatal(err)
}
if !testSubsystem() {
t.Error("should be active")
}
// test option level experimental
subsystemOption.ReleaseLevel = ReleaseLevelExperimental
err = SetConfigOption(releaseLevelKey, ReleaseLevelNameStable)
if err != nil {
t.Fatal(err)
}
if testSubsystem() {
t.Error("should be inactive")
}
err = SetConfigOption(releaseLevelKey, ReleaseLevelNameBeta)
if err != nil {
t.Fatal(err)
}
if testSubsystem() {
t.Error("should be inactive")
}
err = SetConfigOption(releaseLevelKey, ReleaseLevelNameExperimental)
if err != nil {
t.Fatal(err)
}
if !testSubsystem() {
t.Error("should be active")
}
}
func BenchmarkGetAsStringCached(b *testing.B) {
// reset
options = make(map[string]*Option)
// Setup
err := parseAndReplaceConfig(`{
"monkey": "banana"
}`)
if err != nil {
b.Fatal(err)
}
monkey := GetAsString("monkey", "no banana")
// Reset timer for precise results
b.ResetTimer()
// Start benchmark
for range b.N {
monkey()
}
}
func BenchmarkGetAsStringRefetch(b *testing.B) {
// Setup
err := parseAndReplaceConfig(`{
"monkey": "banana"
}`)
if err != nil {
b.Fatal(err)
}
// Reset timer for precise results
b.ResetTimer()
// Start benchmark
for range b.N {
getValueCache("monkey", nil, OptTypeString)
}
}
func BenchmarkGetAsIntCached(b *testing.B) {
// Setup
err := parseAndReplaceConfig(`{
"elephant": 1
}`)
if err != nil {
b.Fatal(err)
}
elephant := GetAsInt("elephant", -1)
// Reset timer for precise results
b.ResetTimer()
// Start benchmark
for range b.N {
elephant()
}
}
func BenchmarkGetAsIntRefetch(b *testing.B) {
// Setup
err := parseAndReplaceConfig(`{
"elephant": 1
}`)
if err != nil {
b.Fatal(err)
}
// Reset timer for precise results
b.ResetTimer()
// Start benchmark
for range b.N {
getValueCache("elephant", nil, OptTypeInt)
}
}