From c3263c03776401ad1263a9fb8f5a44a8ed44d61b Mon Sep 17 00:00:00 2001 From: Julien Dessaux Date: Wed, 17 Nov 2021 10:13:06 +0100 Subject: Refactored package structure --- config/action.go | 54 ------- config/action_test.go | 105 ------------ config/app.go | 54 ------- config/app_test.go | 90 ----------- config/command.go | 32 ---- config/command_test.go | 51 ------ config/config.go | 64 -------- config/config_test.go | 221 -------------------------- config/game.go | 63 -------- config/game_test.go | 107 ------------- config/menu.go | 113 ------------- config/menu_test.go | 71 --------- config/test_data/duplicate_game.yaml | 30 ---- config/test_data/duplicate_menu.yaml | 28 ---- config/test_data/fake_nethack_directory/.keep | 0 config/test_data/invalid_app.yaml | 11 -- config/test_data/invalid_game.yaml | 21 --- config/test_data/invalid_menus.yaml | 19 --- config/test_data/invalid_yaml | 1 - config/test_data/minimal.yaml | 18 --- config/test_data/no_anonymous_menu.yaml | 18 --- config/test_data/no_logged_in_menu.yaml | 18 --- config/test_data/non_existant_chopts.yaml | 18 --- config/test_data/non_existant_game.yaml | 18 --- config/test_data/non_existant_menu.yaml | 18 --- config/test_data/not_enough_menus.yaml | 13 -- config/test_data/unreachable_game.yaml | 25 --- config/test_data/unreachable_menu.yaml | 23 --- 28 files changed, 1304 deletions(-) delete mode 100644 config/action.go delete mode 100644 config/action_test.go delete mode 100644 config/app.go delete mode 100644 config/app_test.go delete mode 100644 config/command.go delete mode 100644 config/command_test.go delete mode 100644 config/config.go delete mode 100644 config/config_test.go delete mode 100644 config/game.go delete mode 100644 config/game_test.go delete mode 100644 config/menu.go delete mode 100644 config/menu_test.go delete mode 100644 config/test_data/duplicate_game.yaml delete mode 100644 config/test_data/duplicate_menu.yaml delete mode 100644 config/test_data/fake_nethack_directory/.keep delete mode 100644 config/test_data/invalid_app.yaml delete mode 100644 config/test_data/invalid_game.yaml delete mode 100644 config/test_data/invalid_menus.yaml delete mode 100644 config/test_data/invalid_yaml delete mode 100644 config/test_data/minimal.yaml delete mode 100644 config/test_data/no_anonymous_menu.yaml delete mode 100644 config/test_data/no_logged_in_menu.yaml delete mode 100644 config/test_data/non_existant_chopts.yaml delete mode 100644 config/test_data/non_existant_game.yaml delete mode 100644 config/test_data/non_existant_menu.yaml delete mode 100644 config/test_data/not_enough_menus.yaml delete mode 100644 config/test_data/unreachable_game.yaml delete mode 100644 config/test_data/unreachable_menu.yaml (limited to 'config') diff --git a/config/action.go b/config/action.go deleted file mode 100644 index c63308e..0000000 --- a/config/action.go +++ /dev/null @@ -1,54 +0,0 @@ -package config - -import ( - "strings" - - "github.com/pkg/errors" -) - -func validateAction(action string) error { - tokens := strings.Split(action, " ") - switch tokens[0] { - case "chmail": - if len(tokens) != 1 { - return errors.New("chmail action takes no arguments") - } - case "login": - if len(tokens) != 1 { - return errors.New("login action takes no arguments") - } - case "menu": - if len(tokens) != 2 { - return errors.New("menu action takes exactly one argument") - } - // menu existence is tested in global config - case "passwd": - if len(tokens) != 1 { - return errors.New("passwd action takes no arguments") - } - case "play": - if len(tokens) != 2 { - return errors.New("play action takes exactly one argument") - } - // game existence is tested in global config - case "register": - if len(tokens) != 1 { - return errors.New("register action takes no arguments") - } - case "replay": - if len(tokens) != 1 { - return errors.New("replay action takes no arguments") - } - case "watch": - if len(tokens) != 1 { - return errors.New("watch action takes no arguments") - } - case "quit": - if len(tokens) != 1 { - return errors.New("quit action takes no arguments") - } - default: - return errors.New("Invalid action : " + tokens[0]) - } - return nil -} diff --git a/config/action_test.go b/config/action_test.go deleted file mode 100644 index dbc6ae3..0000000 --- a/config/action_test.go +++ /dev/null @@ -1,105 +0,0 @@ -package config - -import "testing" - -func TestActionValidate(t *testing.T) { - // Empty action - menuEntry := MenuEntry{Key: "l", Label: "label", Action: ""} - if err := menuEntry.validate(); err == nil { - t.Fatal("An action cannot be empty") - } - // Invalid action - menuEntry = MenuEntry{Key: "l", Label: "label", Action: "invalid"} - if err := menuEntry.validate(); err == nil { - t.Fatal("An action must be valid") - } - // chmail - menuEntry = MenuEntry{Key: "l", Label: "label", Action: "chmail a"} - if err := menuEntry.validate(); err == nil { - t.Fatal("chmail action does not take arguments") - } - menuEntry = MenuEntry{Key: "l", Label: "label", Action: "chmail"} - if err := menuEntry.validate(); err != nil { - t.Fatalf("chmail action without arguments is valid\nerror: +%v", err) - } - // login - menuEntry = MenuEntry{Key: "l", Label: "label", Action: "login a"} - if err := menuEntry.validate(); err == nil { - t.Fatal("login action does not take arguments") - } - menuEntry = MenuEntry{Key: "l", Label: "label", Action: "login"} - if err := menuEntry.validate(); err != nil { - t.Fatalf("login action without arguments is valid\nerror: +%v", err) - } - // menu - menuEntry = MenuEntry{Key: "l", Label: "label", Action: "menu"} - if err := menuEntry.validate(); err == nil { - t.Fatal("menu action takes exactly one argument") - } - menuEntry = MenuEntry{Key: "l", Label: "label", Action: "menu test plop"} - if err := menuEntry.validate(); err == nil { - t.Fatal("menu action takes exactly one argument") - } - menuEntry = MenuEntry{Key: "l", Label: "label", Action: "menu test"} - if err := menuEntry.validate(); err != nil { - t.Fatalf("menu action with one argument is valid\nerror: +%v", err) - } - // passwd - menuEntry = MenuEntry{Key: "l", Label: "label", Action: "passwd a"} - if err := menuEntry.validate(); err == nil { - t.Fatal("passwd action does not take arguments") - } - menuEntry = MenuEntry{Key: "l", Label: "label", Action: "passwd"} - if err := menuEntry.validate(); err != nil { - t.Fatalf("passwd action without arguments is valid\nerror: +%v", err) - } - // play - menuEntry = MenuEntry{Key: "l", Label: "label", Action: "play"} - if err := menuEntry.validate(); err == nil { - t.Fatal("play action takes exactly one argument") - } - menuEntry = MenuEntry{Key: "l", Label: "label", Action: "play test plop"} - if err := menuEntry.validate(); err == nil { - t.Fatal("play action takes exactly one argument") - } - menuEntry = MenuEntry{Key: "l", Label: "label", Action: "play test"} - if err := menuEntry.validate(); err != nil { - t.Fatalf("play action with one argument is valid\nerror: +%v", err) - } - // register - menuEntry = MenuEntry{Key: "l", Label: "label", Action: "register a"} - if err := menuEntry.validate(); err == nil { - t.Fatal("register action does not take arguments") - } - menuEntry = MenuEntry{Key: "l", Label: "label", Action: "register"} - if err := menuEntry.validate(); err != nil { - t.Fatalf("register action without arguments is valid\nerror: +%v", err) - } - // replay - menuEntry = MenuEntry{Key: "l", Label: "label", Action: "replay a"} - if err := menuEntry.validate(); err == nil { - t.Fatal("replay action does not take arguments") - } - menuEntry = MenuEntry{Key: "l", Label: "label", Action: "replay"} - if err := menuEntry.validate(); err != nil { - t.Fatalf("replay action without arguments is valid\nerror: +%v", err) - } - // watch - menuEntry = MenuEntry{Key: "l", Label: "label", Action: "watch a"} - if err := menuEntry.validate(); err == nil { - t.Fatal("watch action does not take arguments") - } - menuEntry = MenuEntry{Key: "l", Label: "label", Action: "watch"} - if err := menuEntry.validate(); err != nil { - t.Fatalf("watch action without arguments is valid\nerror: +%v", err) - } - // quit - menuEntry = MenuEntry{Key: "l", Label: "label", Action: "quit a"} - if err := menuEntry.validate(); err == nil { - t.Fatal("quit action does not take arguments") - } - menuEntry = MenuEntry{Key: "l", Label: "label", Action: "quit"} - if err := menuEntry.validate(); err != nil { - t.Fatalf("quit action without arguments is valid\nerror: +%v", err) - } -} diff --git a/config/app.go b/config/app.go deleted file mode 100644 index 2eefb98..0000000 --- a/config/app.go +++ /dev/null @@ -1,54 +0,0 @@ -package config - -import ( - "os" - - "github.com/pkg/errors" - "golang.org/x/sys/unix" -) - -// App struct contains the configuration for this application -type App struct { - // WorkingDirectory is the program working directory where the user data, save files and scores are stored - WorkingDirectory string `yaml:"WorkingDirectory"` - // MaxUsers is the maximum amount of registered users to allow - MaxUsers int `yaml:"MaxUsers"` - // AllowRegistration allows registration of new users - AllowRegistration bool `yaml:"AllowRegistration"` - // MaxNickLen Maximum length for a nickname - MaxNickLen int `yaml:"MaxNickLen"` - // MenuMaxIdleTime is the maximum number of seconds a user can be idle on the menu before the program exits - MenuMaxIdleTime int `yaml:"MenuMaxIdleTime"` - // PostLoginCommands is the list of commands to execute upon login, like creating save directories for games - PostLoginCommands []string `yaml:"PostLoginCommands"` -} - -func (a *App) validate() error { - // WorkingDirectory - if err := os.MkdirAll(a.WorkingDirectory, 0700); err != nil { - return errors.Wrapf(err, "Invalid WorkingDirectory : %s", a.WorkingDirectory) - } - if err := unix.Access(a.WorkingDirectory, unix.W_OK|unix.R_OK|unix.X_OK); err != nil { - return errors.Wrapf(err, "invalid WorkingDirectory : %s", a.WorkingDirectory) - } - // MaxUsers - if a.MaxUsers <= 0 { - return errors.New("MaxUsers must be a positive integer") - } - // AllowRegistration is just a bool, nothing to validate - // MaxNickLen - if a.MaxNickLen <= 0 { - return errors.New("MaxNickLen must be a positive integer") - } - // MenuMaxIdleTime - if a.MenuMaxIdleTime <= 0 { - return errors.New("MenuMaxIdleTime must be a positive integer") - } - // PostLoginCommands - for i := 0; i < len(a.PostLoginCommands); i++ { - if err := validateCommand(a.PostLoginCommands[i]); err != nil { - return errors.Wrap(err, "Failed to validate PostLoginCommands") - } - } - return nil -} diff --git a/config/app_test.go b/config/app_test.go deleted file mode 100644 index 5a6cf41..0000000 --- a/config/app_test.go +++ /dev/null @@ -1,90 +0,0 @@ -package config - -import ( - "os" - "testing" -) - -func TestAppvalidate(t *testing.T) { - // WorkingDirectory - t.Cleanup(func() { os.RemoveAll("no_permission/") }) - if err := os.Mkdir("no_permission/", 0000); err != nil { - t.Fatal("Could not create test directory") - } - app := App{WorkingDirectory: "no_permission/cannot_work"} - if err := app.validate(); err == nil { - t.Fatal("no_permission/cannot_wor/k should not be a valid working directory") - } - app = App{WorkingDirectory: "no_permission/"} - if err := app.validate(); err == nil { - t.Fatal("no_permission/ should not be a valid working directory") - } - - // MaxUsers - t.Cleanup(func() { os.RemoveAll("var/") }) - app = App{ - WorkingDirectory: "var/", - MaxUsers: 0, - } - if err := app.validate(); err == nil { - t.Fatal("Negative MaxUsers should not be valid") - } - - // AllowRegistration is just a bool, nothing to test - - // MaxNickLen - t.Cleanup(func() { os.RemoveAll("var/") }) - app = App{ - WorkingDirectory: "var/", - MaxUsers: 1, - MaxNickLen: 0, - } - if err := app.validate(); err == nil { - t.Fatal("Negative or zero MaxNickLen should not be valid.") - } - - //MenuMaxIdleTime - t.Cleanup(func() { os.RemoveAll("var/") }) - app = App{ - WorkingDirectory: "var/", - MaxUsers: 512, - MaxNickLen: 15, - MenuMaxIdleTime: 0, - } - if err := app.validate(); err == nil { - t.Fatal("Negative or zero MenuMaxIdleTime should not be valid.") - } - - //PostLoginCommands are mostly tested from command_test.go - app = App{ - WorkingDirectory: "var/", - MaxUsers: 512, - MaxNickLen: 15, - MenuMaxIdleTime: 60, - } - if err := app.validate(); err != nil { - t.Fatal("Empty PostLoginCommands list should be valid") - } - app = App{ - WorkingDirectory: "var/", - MaxUsers: 512, - MaxNickLen: 15, - MenuMaxIdleTime: 60, - PostLoginCommands: []string{"invalid"}, - } - if err := app.validate(); err == nil { - t.Fatal("Invalid command in PostLoginCommands should not be valid") - } - - // A valid App - app = App{ - WorkingDirectory: "var/", - MaxUsers: 512, - MaxNickLen: 15, - MenuMaxIdleTime: 60, - PostLoginCommands: []string{"wait"}, - } - if err := app.validate(); err != nil { - t.Fatal("A valid app should pass") - } -} diff --git a/config/command.go b/config/command.go deleted file mode 100644 index 34142cd..0000000 --- a/config/command.go +++ /dev/null @@ -1,32 +0,0 @@ -package config - -import ( - "strings" - - "github.com/pkg/errors" -) - -func validateCommand(cmd string) error { - tokens := strings.Split(cmd, " ") - switch tokens[0] { - case "cp": - if len(tokens) != 3 { - return errors.New("cp command takes exactly two arguments") - } - case "exec": - if len(tokens) <= 1 { - return errors.New("exec command needs arguments") - } - case "mkdir": - if len(tokens) != 2 { - return errors.New("mkdir command takes exactly one argument") - } - case "wait": - if len(tokens) != 1 { - return errors.New("wait command takes no arguments") - } - default: - return errors.New("Invalid command : " + tokens[0]) - } - return nil -} diff --git a/config/command_test.go b/config/command_test.go deleted file mode 100644 index 7e07956..0000000 --- a/config/command_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package config - -import "testing" - -func TestCommandValidate(t *testing.T) { - // Empty command - if err := validateCommand(""); err == nil { - t.Fatal("An command cannot be empty") - } - // invalid command - if err := validateCommand("invalid"); err == nil { - t.Fatal("An command cannot be empty") - } - // cp - if err := validateCommand("cp"); err == nil { - t.Fatal("cp command needs arguments") - } - if err := validateCommand("cp test"); err == nil { - t.Fatal("cp command needs exactly 2 arguments") - } - if err := validateCommand("cp test test test"); err == nil { - t.Fatal("cp command needs exactly 2 arguments") - } - if err := validateCommand("exec test test"); err != nil { - t.Fatal("valid exec command should be accepted") - } - // exec - if err := validateCommand("exec"); err == nil { - t.Fatal("exec command needs arguments") - } - if err := validateCommand("exec test"); err != nil { - t.Fatal("valid exec command should be accepted") - } - // mkdir - if err := validateCommand("mkdir"); err == nil { - t.Fatal("mkdir command needs exactly 1 argument") - } - if err := validateCommand("mkdir testtest "); err == nil { - t.Fatal("mkdir command needs exactly 1 argument") - } - if err := validateCommand("mkdir test"); err != nil { - t.Fatal("valid mkdir command should be accepted") - } - // wait - if err := validateCommand("wait test"); err == nil { - t.Fatal("wait command needs no arguments") - } - if err := validateCommand("wait"); err != nil { - t.Fatal("valid wait command should be accepted") - } -} diff --git a/config/config.go b/config/config.go deleted file mode 100644 index b68a26a..0000000 --- a/config/config.go +++ /dev/null @@ -1,64 +0,0 @@ -package config - -import ( - "os" - - "github.com/pkg/errors" - "gopkg.in/yaml.v3" -) - -type Config struct { - // AppConfig is the application level configuration entries - App App `yaml:"App"` - // Menus is the list of menus. The first one is the default menu for an anonymous user, the second one is the default menu for an authenticated user - Menus map[string]Menu `yaml:"Menus"` - // Games is the list of games. - Games map[string]Game `yaml:"Games"` -} - -func (c *Config) validate() error { - // App - if err := c.App.validate(); err != nil { - return err - } - // Menus - if len(c.Menus) < 2 { - return errors.New("A valid configuration needs at least two menu entries named anonymous and logged_in") - } - for k, v := range c.Menus { - if err := v.validate(k); err != nil { - return err - } - } - // Games - for k, v := range c.Games { - if err := v.validate(k); err != nil { - return err - } - } - return nil -} - -// LoadFile loads the c from a given file -func LoadFile(path string) (*Config, error) { - var c *Config - f, errOpen := os.Open(path) - if errOpen != nil { - return nil, errors.Wrapf(errOpen, "Failed to open configuration file %s", path) - } - defer f.Close() - decoder := yaml.NewDecoder(f) - if err := decoder.Decode(&c); err != nil { - return nil, errors.Wrap(err, "Failed to decode configuration file") - } - if err := c.validate(); err != nil { - return nil, errors.Wrap(err, "Failed to validate configuration") - } - // If all looks good we validate menu consistency - for _, v := range c.Menus { - if err := v.validateConsistency(c); err != nil { - return nil, errors.Wrap(err, "Failed menu consistency checks") - } - } - return c, nil -} diff --git a/config/config_test.go b/config/config_test.go deleted file mode 100644 index dfa8237..0000000 --- a/config/config_test.go +++ /dev/null @@ -1,221 +0,0 @@ -package config - -import ( - "os" - "reflect" - "testing" -) - -func TestLoadFile(t *testing.T) { - // Non existant file - _, err := LoadFile("test_data/non-existant") - if err == nil { - t.Fatal("non-existant config file failed without error") - } - - // Invalid yaml file - _, err = LoadFile("test_data/invalid_yaml") - if err == nil { - t.Fatal("invalid_yaml config file failed without error") - } - - // Minimal yaml file - want := Config{ - App: App{ - WorkingDirectory: "var/", - MaxUsers: 1, - AllowRegistration: true, - MaxNickLen: 15, - MenuMaxIdleTime: 600, - }, - Menus: map[string]Menu{ - "anonymous": Menu{ - MenuEntries: []MenuEntry{ - MenuEntry{ - Key: "q", - Label: "quit", - Action: "quit", - }, - }, - }, - "logged_in": Menu{ - MenuEntries: []MenuEntry{ - MenuEntry{ - Key: "q", - Label: "quit", - Action: "quit", - }, - }, - }, - }, - } - config, err := LoadFile("test_data/minimal.yaml") - if err != nil { - t.Fatalf("minimal example failed with error : %v", err) - } - if config != nil && !reflect.DeepEqual(want, *config) { - t.Fatalf("minimal example failed:\nwant:%+v\ngot: %+v", want, *config) - } - t.Cleanup(func() { os.RemoveAll("var/") }) - // Invalid App - if _, err := LoadFile("test_data/invalid_app.yaml"); err == nil { - t.Fatal("Invalid App entry should fail to load") - } - // Not enough menus - if _, err := LoadFile("test_data/not_enough_menus.yaml"); err == nil { - t.Fatal("not enough menu entries should fail to load") - } - // Invalid Menus - if _, err := LoadFile("test_data/invalid_menus.yaml"); err == nil { - t.Fatal("Invalid menu entry should fail to load") - } - // no anonymous Menu - if _, err := LoadFile("test_data/no_anonymous_menu.yaml"); err == nil { - t.Fatal("Invalid menu entry should fail to load") - } - // no logged_in Menu - if _, err := LoadFile("test_data/no_logged_in_menu.yaml"); err == nil { - t.Fatal("Invalid menu entry should fail to load") - } - // duplicate menu - if _, err := LoadFile("test_data/duplicate_menu.yaml"); err == nil { - t.Fatal("duplicate menu should fail to load") - } - // non existant menu action referenced - if _, err := LoadFile("test_data/non_existant_menu.yaml"); err == nil { - t.Fatal("menu entry referencing a non existant menu should fail to load") - } - // non existant game referenced in play action - if _, err := LoadFile("test_data/non_existant_game.yaml"); err == nil { - t.Fatal("menu entry referencing a non existant play action should fail to load") - } - // unreachable menu - if _, err := LoadFile("test_data/unreachable_menu.yaml"); err == nil { - t.Fatal("unreachable menu should fail to load") - } - // invalid game - if _, err := LoadFile("test_data/invalid_game.yaml"); err == nil { - t.Fatal("invalid game should fail to load") - } - // unreachable game - if _, err := LoadFile("test_data/unreachable_game.yaml"); err == nil { - t.Fatal("unreachable game should fail to load") - } - // duplicate game - if _, err := LoadFile("test_data/duplicate_game.yaml"); err == nil { - t.Fatal("unreachable game should fail to load") - } - - // Complexe example - want = Config{ - App: App{ - WorkingDirectory: "var/", - MaxUsers: 512, - AllowRegistration: true, - MaxNickLen: 15, - MenuMaxIdleTime: 600, - PostLoginCommands: []string{ - "mkdir %w/userdata/%u", - "mkdir %w/userdata/%u/dumplog", - "mkdir %w/userdata/%u/ttyrec", - }, - }, - Menus: map[string]Menu{ - "anonymous": Menu{ - Banner: "Shell Game Launcher - Anonymous access%n======================================", - MenuEntries: []MenuEntry{ - MenuEntry{ - Key: "l", - Label: "login", - Action: "login", - }, - MenuEntry{ - Key: "r", - Label: "register", - Action: "register", - }, - MenuEntry{ - Key: "w", - Label: "watch", - Action: "watch_menu", - }, - MenuEntry{ - Key: "q", - Label: "quit", - Action: "quit", - }, - }, - }, - "logged_in": Menu{ - Banner: "Shell Game Launcher%n===================", - MenuEntries: []MenuEntry{ - MenuEntry{ - Key: "p", - Label: "play Nethack 3.7", - Action: "play nethack3.7", - }, - MenuEntry{ - Key: "o", - Label: "edit game options", - Action: "menu options", - }, - MenuEntry{ - Key: "w", - Label: "watch", - Action: "watch", - }, - MenuEntry{ - Key: "r", - Label: "replay", - Action: "replay", - }, - MenuEntry{ - Key: "c", - Label: "change password", - Action: "passwd", - }, - MenuEntry{ - Key: "m", - Label: "change email", - Action: "chmail", - }, - MenuEntry{ - Key: "q", - Label: "quit", - Action: "quit", - }, - }, - }, - "options": Menu{ - Banner: "Options%n=======", - MenuEntries: []MenuEntry{ - MenuEntry{ - Key: "z", - Label: "back", - Action: "menu logged_in", - }, - }, - }, - }, - Games: map[string]Game{ - "nethack3.7": Game{ - ChrootPath: "test_data/fake_nethack_directory", - FileMode: "0666", - Commands: []string{ - "cp /games/var/save/%u%n.gz /games/var/save/%u%n.gz.bak", - "exec /games/nethack -u %n", - }, - Env: map[string]string{ - "NETHACKOPTIONS": "@%ruserdata/%n/%n.nhrc", - }, - }, - }, - } - config, err = LoadFile("../example/complete.yaml") - if err != nil { - t.Fatalf("complete example failed with error : %v", err) - } - if config != nil && !reflect.DeepEqual(want, *config) { - t.Fatalf("complete example failed:\nwant:%+v\ngot: %+v", want, *config) - } -} diff --git a/config/game.go b/config/game.go deleted file mode 100644 index b5f4d39..0000000 --- a/config/game.go +++ /dev/null @@ -1,63 +0,0 @@ -package config - -import ( - "regexp" - - "github.com/pkg/errors" - "golang.org/x/sys/unix" -) - -var reValidGameName = regexp.MustCompile(`^[\w\._]+$`) -var reValidFileMode = regexp.MustCompile(`^0[\d]{3}$`) -var reSpace = regexp.MustCompile(`^\s$`) - -// Game struct containers the configuration for a game -type Game struct { - // ChrootPath is the chroot path for the game - ChrootPath string `yaml:"ChrootPath"` - // FileMode is the file mode to use when copying files - FileMode string `yaml:"FileMode"` - // Commands is the command list - Commands []string `yaml:"Commands"` - // Env is the environment in which to exec the commands - Env map[string]string `yaml:"Env"` -} - -func (g *Game) validate(name string) error { - // Game name - if ok := reValidGameName.MatchString(name); !ok { - return errors.New("Invalid Game name, must match regex `^[\\w\\._]+$` : " + name) - } - // ChrootPath TODO - if err := unix.Access(g.ChrootPath, unix.R_OK|unix.X_OK); err != nil { - return errors.Wrapf(err, "Invalid ChrootPath : %s", g.ChrootPath) - } - // FileMode - if ok := reValidFileMode.MatchString(g.FileMode); !ok { - return errors.New("Invalid File Mode, must match regex `^0[\\d]{3}$` : " + name) - } - // Commands - if len(g.Commands) == 0 { - return errors.New("Invalid game " + name + " has no commands") - } - for i := 0; i < len(g.Commands); i++ { - if err := validateCommand(g.Commands[i]); err != nil { - return errors.Wrapf(err, "Failed to validate Commands for game %s", name) - } - } - // Env - for k, _ := range g.Env { - for _, c := range k { - switch c { - case '=': - return errors.New("Environment variable key must not contain equal sign") - case '\000': - return errors.New("Environment variable key must not contain null character") - } - if reSpace.MatchString(string(c)) { - return errors.New("Environment variable key must not contain spaces") - } - } - } - return nil -} diff --git a/config/game_test.go b/config/game_test.go deleted file mode 100644 index b3bf107..0000000 --- a/config/game_test.go +++ /dev/null @@ -1,107 +0,0 @@ -package config - -import "testing" - -func TestGameValidate(t *testing.T) { - // Game name - game := Game{} - if err := game.validate("invalid game name because of spaces"); err == nil { - t.Fatal("game name with spaces should not be valid") - } - // ChrootPath - game = Game{ChrootPath: "test_data/non_existant"} - if err := game.validate("test"); err == nil { - t.Fatal("non_existant ChrootPath should not be valid") - } - // FileMode - game = Game{ - ChrootPath: "test_data/fake_nethack_directory", - } - if err := game.validate("test"); err == nil { - t.Fatal("Invalid FileMode should not be valid") - } - game = Game{ - ChrootPath: "test_data/fake_nethack_directory", - FileMode: "abcd", - } - if err := game.validate("test"); err == nil { - t.Fatal("Invalid FileMode should not be valid") - } - game = Game{ - ChrootPath: "test_data/fake_nethack_directory", - FileMode: "777", - } - if err := game.validate("test"); err == nil { - t.Fatal("Invalid FileMode should not be valid") - } - // Commands are mostly tested from command_test.go - game = Game{ - ChrootPath: "test_data/fake_nethack_directory", - FileMode: "0777", - } - if err := game.validate("test"); err == nil { - t.Fatal("Empty Commands list should not be valid") - } - game = Game{ - ChrootPath: "test_data/fake_nethack_directory", - FileMode: "0777", - Commands: []string{"invalid"}, - } - if err := game.validate("test"); err == nil { - t.Fatal("Invalid command in Commands should not be valid") - } - // Env - game = Game{ - ChrootPath: "test_data/fake_nethack_directory", - FileMode: "0777", - Commands: []string{"wait"}, - } - if err := game.validate("test"); err != nil { - t.Fatal("Empty env list should be valid") - } - game = Game{ - ChrootPath: "test_data/fake_nethack_directory", - FileMode: "0777", - Commands: []string{"wait"}, - Env: map[string]string{ - "test invalid": "test", - }, - } - if err := game.validate("test"); err == nil { - t.Fatal("Spaces in environnement variable name are invalid") - } - game = Game{ - ChrootPath: "test_data/fake_nethack_directory", - FileMode: "0777", - Commands: []string{"wait"}, - Env: map[string]string{ - "test\000invalid": "test", - }, - } - if err := game.validate("test"); err == nil { - t.Fatal("null character in environnement variable name are invalid") - } - game = Game{ - ChrootPath: "test_data/fake_nethack_directory", - FileMode: "0777", - Commands: []string{"wait"}, - Env: map[string]string{ - "test=invalid": "test", - }, - } - if err := game.validate("test"); err == nil { - t.Fatal("equals symbol in environnement variable name are invalid") - } - // Valid Game entry - game = Game{ - ChrootPath: "test_data/fake_nethack_directory", - FileMode: "0777", - Commands: []string{"wait"}, - Env: map[string]string{ - "test": "test", - }, - } - if err := game.validate("test"); err != nil { - t.Fatalf("Valid game entry should pass but got error %s", err) - } -} diff --git a/config/menu.go b/config/menu.go deleted file mode 100644 index 7321c7b..0000000 --- a/config/menu.go +++ /dev/null @@ -1,113 +0,0 @@ -package config - -import ( - "regexp" - "strings" - - "github.com/pkg/errors" -) - -var reValidMenuName = regexp.MustCompile(`^[\w\._]+$`) -var reValidKey = regexp.MustCompile(`^\w$`) - -// Menu struct describes a screen menu -type Menu struct { - // Banner is the banner to display before the menu - Banner string `yaml:"Banner"` - // Commands is the list of commands in the menu - MenuEntries []MenuEntry `yaml:"MenuEntries"` -} - -// MenuEntry struct describes a menu entry -type MenuEntry struct { - // Key is the key associated with the action. We need to store it as a string because of how yaml unmarshal works - Key string `yaml:"Key"` - // Label is the text displayed on the menu - Label string `yaml:"Label"` - // Action is the action executed when the menu entry is selected - Action string `yaml:"Action"` -} - -func (m *Menu) validate(name string) error { - // validate name - if ok := reValidMenuName.MatchString(name); !ok { - return errors.New("Invalid menu name, must be an alphanumeric word and match regex `^[\\w\\._]+$` : " + name) - } - // Banner is just any string, nothing to validate - // MenuEntries - if len(m.MenuEntries) == 0 { - return errors.New("A Menu needs MenuEntries to be valid") - } - // Duplicate detection is natively handled by the yaml parser - for i := 0; i < len(m.MenuEntries); i++ { - m.MenuEntries[i].validate() - if m.MenuEntries[i].Action == "menu "+name { - return errors.New("A menu shall not loop on itself") - } - } - // Loop test - return nil -} - -func (m *Menu) validateConsistency(c *Config) error { - // Necessary menus - if _, ok := c.Menus["anonymous"]; !ok { - return errors.New("No anonymous menu declared") - } - if _, ok := c.Menus["logged_in"]; !ok { - return errors.New("No logged_in menu declared") - } - // Validate actions - menus := map[string]bool{ - "anonymous": true, - "logged_in": true, - } - playable := make(map[string]bool) - for k, v := range c.Menus { - for _, e := range v.MenuEntries { - tokens := strings.Split(e.Action, " ") - switch tokens[0] { - case "menu": - if _, ok := c.Menus[tokens[1]]; ok { - menus[tokens[1]] = true - } else { - return errors.New("menu action " + tokens[1] + " in menu " + k + " does not exist") - } - case "play": - if _, ok := c.Games[tokens[1]]; ok { - playable[tokens[1]] = true - } else { - return errors.New("play action " + tokens[1] + " in menu " + k + " does not exist") - } - } - } - } - // Check for unreachables - for k, _ := range c.Menus { - if _, ok := menus[k]; !ok { - return errors.New("unreachable menu : " + k) - } - } - for k, _ := range c.Games { - if _, ok := playable[k]; !ok { - return errors.New("unplayable game : " + k) - } - } - return nil -} - -func (m *MenuEntry) validate() error { - // Key - if ok := reValidKey.MatchString(m.Key); !ok { - return errors.New("Invalid Key, must be exactly one alphanumeric character and match regex `^\\w$` : " + m.Key) - } - // Label - if len(m.Label) <= 0 { - return errors.New("Invalid Label, cannot be empty") - } - // Action - if err := validateAction(m.Action); err != nil { - return errors.Wrap(err, "Invalid Action in MenuEntry") - } - return nil -} diff --git a/config/menu_test.go b/config/menu_test.go deleted file mode 100644 index e9abef1..0000000 --- a/config/menu_test.go +++ /dev/null @@ -1,71 +0,0 @@ -package config - -import "testing" - -func TestMenuValidate(t *testing.T) { - // menu name - menu := Menu{} - if err := menu.validate(""); err == nil { - t.Fatal("Empty menu name is not valid") - } - if err := menu.validate("test test"); err == nil { - t.Fatal("non alphanumeric menu name is not valid") - } - // Banner is just any string, nothing to validate - // MenuEntries are mostly tested bellow - menu = Menu{} - if err := menu.validate("test"); err == nil { - t.Fatal("A menu without menu entries should not be valid") - } - // loop menu - menu = Menu{ - MenuEntries: []MenuEntry{ - MenuEntry{ - Key: "a", - Label: "test", - Action: "menu test", - }, - }, - } - if err := menu.validate("test"); err == nil { - t.Fatal("A menu should not be able to loop on itself") - } - // A valid menu - menu = Menu{ - MenuEntries: []MenuEntry{ - MenuEntry{ - Key: "a", - Label: "test", - Action: "quit", - }, - }, - } - if err := menu.validate("test"); err != nil { - t.Fatal("A valid menu should pass") - } -} - -func TestMenuEntryValidate(t *testing.T) { - // Key - menuEntry := MenuEntry{} - if err := menuEntry.validate(); err == nil { - t.Fatal("A Key cannot be empty") - } - menuEntry = MenuEntry{Key: "ab"} - if err := menuEntry.validate(); err == nil { - t.Fatal("A Key should be only one character") - } - menuEntry = MenuEntry{Key: " "} - if err := menuEntry.validate(); err == nil { - t.Fatal("A Key should be a printable character") - } - // Label - menuEntry = MenuEntry{ - Key: "l", - Label: "", - } - if err := menuEntry.validate(); err == nil { - t.Fatal("A Label cannot be empty") - } - // Actions are tested in action_test.go -} diff --git a/config/test_data/duplicate_game.yaml b/config/test_data/duplicate_game.yaml deleted file mode 100644 index f01a017..0000000 --- a/config/test_data/duplicate_game.yaml +++ /dev/null @@ -1,30 +0,0 @@ -App: - WorkingDirectory: var/ - MaxUsers: 1 - AllowRegistration: true - MaxNickLen: 15 - MenuMaxIdleTime: 600 - -Menus: - anonymous: - MenuEntries: - - Key: q - Label: quit - Action: quit - logged_in: - MenuEntries: - - Key: p - Label: play - Action: play test - -Games: - test: - ChrootPath: test_data/fake_nethack_directory - FileMode: 0777 - Commands: - - wait - test: - ChrootPath: test_data/fake_nethack_directory - FileMode: 0777 - Commands: - - wait diff --git a/config/test_data/duplicate_menu.yaml b/config/test_data/duplicate_menu.yaml deleted file mode 100644 index 3dbefb7..0000000 --- a/config/test_data/duplicate_menu.yaml +++ /dev/null @@ -1,28 +0,0 @@ -App: - WorkingDirectory: var/ - MaxUsers: 1 - AllowRegistration: true - MaxNickLen: 15 - MenuMaxIdleTime: 600 - -Menus: - anonymous: - MenuEntries: - - Key: t - Label: test - Action: menu test - logged_in: - MenuEntries: - - Key: q - Label: quit - Action: quit - test: - MenuEntries: - - Key: q - Label: quit - Action: quit - test: - MenuEntries: - - Key: a - Label: login - Action: login diff --git a/config/test_data/fake_nethack_directory/.keep b/config/test_data/fake_nethack_directory/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/config/test_data/invalid_app.yaml b/config/test_data/invalid_app.yaml deleted file mode 100644 index ed236ea..0000000 --- a/config/test_data/invalid_app.yaml +++ /dev/null @@ -1,11 +0,0 @@ -Menus: - anonymous: - MenuEntries: - - Key: q - Label: quit - Action: quit - logged_in: - MenuEntries: - - Key: q - Label: quit - Action: quit diff --git a/config/test_data/invalid_game.yaml b/config/test_data/invalid_game.yaml deleted file mode 100644 index d58c3ee..0000000 --- a/config/test_data/invalid_game.yaml +++ /dev/null @@ -1,21 +0,0 @@ -App: - WorkingDirectory: var/ - MaxUsers: 1 - AllowRegistration: true - MaxNickLen: 15 - MenuMaxIdleTime: 600 - -Menus: - anonymous: - MenuEntries: - - Key: q - Label: quit - Action: quit - logged_in: - MenuEntries: - - Key: p - Label: play - Action: play test - -Games: - test: diff --git a/config/test_data/invalid_menus.yaml b/config/test_data/invalid_menus.yaml deleted file mode 100644 index 1df5fbf..0000000 --- a/config/test_data/invalid_menus.yaml +++ /dev/null @@ -1,19 +0,0 @@ -App: - WorkingDirectory: var/ - MaxUsers: 1 - AllowRegistration: true - MaxNickLen: 15 - MenuMaxIdleTime: 600 - -Menus: - anonymous: - MenuEntries: - - Key: q - Label: quit - Action: quit - logged_in: - MenuEntries: - - Key: q - Label: quit - Action: quit - test: diff --git a/config/test_data/invalid_yaml b/config/test_data/invalid_yaml deleted file mode 100644 index db1ddad..0000000 --- a/config/test_data/invalid_yaml +++ /dev/null @@ -1 +0,0 @@ -blargh(ads) diff --git a/config/test_data/minimal.yaml b/config/test_data/minimal.yaml deleted file mode 100644 index 22a0a6c..0000000 --- a/config/test_data/minimal.yaml +++ /dev/null @@ -1,18 +0,0 @@ -App: - WorkingDirectory: var/ - MaxUsers: 1 - AllowRegistration: true - MaxNickLen: 15 - MenuMaxIdleTime: 600 - -Menus: - anonymous: - MenuEntries: - - Key: q - Label: quit - Action: quit - logged_in: - MenuEntries: - - Key: q - Label: quit - Action: quit diff --git a/config/test_data/no_anonymous_menu.yaml b/config/test_data/no_anonymous_menu.yaml deleted file mode 100644 index a015160..0000000 --- a/config/test_data/no_anonymous_menu.yaml +++ /dev/null @@ -1,18 +0,0 @@ -App: - WorkingDirectory: var/ - MaxUsers: 1 - AllowRegistration: true - MaxNickLen: 15 - MenuMaxIdleTime: 600 - -Menus: - test: - MenuEntries: - - Key: q - Label: quit - Action: quit - logged_in: - MenuEntries: - - Key: q - Label: quit - Action: quit diff --git a/config/test_data/no_logged_in_menu.yaml b/config/test_data/no_logged_in_menu.yaml deleted file mode 100644 index 43d0054..0000000 --- a/config/test_data/no_logged_in_menu.yaml +++ /dev/null @@ -1,18 +0,0 @@ -App: - WorkingDirectory: var/ - MaxUsers: 1 - AllowRegistration: true - MaxNickLen: 15 - MenuMaxIdleTime: 600 - -Menus: - anonymous: - MenuEntries: - - Key: q - Label: quit - Action: quit - test: - MenuEntries: - - Key: q - Label: quit - Action: quit diff --git a/config/test_data/non_existant_chopts.yaml b/config/test_data/non_existant_chopts.yaml deleted file mode 100644 index d3f796d..0000000 --- a/config/test_data/non_existant_chopts.yaml +++ /dev/null @@ -1,18 +0,0 @@ -App: - WorkingDirectory: var/ - MaxUsers: 1 - AllowRegistration: true - MaxNickLen: 15 - MenuMaxIdleTime: 600 - -Menus: - anonymous: - MenuEntries: - - Key: q - Label: quit - Action: quit - logged_in: - MenuEntries: - - Key: t - Label: test - Action: chopts invalid diff --git a/config/test_data/non_existant_game.yaml b/config/test_data/non_existant_game.yaml deleted file mode 100644 index 9bf6a38..0000000 --- a/config/test_data/non_existant_game.yaml +++ /dev/null @@ -1,18 +0,0 @@ -App: - WorkingDirectory: var/ - MaxUsers: 1 - AllowRegistration: true - MaxNickLen: 15 - MenuMaxIdleTime: 600 - -Menus: - anonymous: - MenuEntries: - - Key: q - Label: quit - Action: quit - logged_in: - MenuEntries: - - Key: t - Label: test - Action: play invalid diff --git a/config/test_data/non_existant_menu.yaml b/config/test_data/non_existant_menu.yaml deleted file mode 100644 index f0f30a3..0000000 --- a/config/test_data/non_existant_menu.yaml +++ /dev/null @@ -1,18 +0,0 @@ -App: - WorkingDirectory: var/ - MaxUsers: 1 - AllowRegistration: true - MaxNickLen: 15 - MenuMaxIdleTime: 600 - -Menus: - anonymous: - MenuEntries: - - Key: t - Label: test - Action: menu invalid - logged_in: - MenuEntries: - - Key: q - Label: quit - Action: quit diff --git a/config/test_data/not_enough_menus.yaml b/config/test_data/not_enough_menus.yaml deleted file mode 100644 index f585f44..0000000 --- a/config/test_data/not_enough_menus.yaml +++ /dev/null @@ -1,13 +0,0 @@ -App: - WorkingDirectory: var/ - MaxUsers: 1 - AllowRegistration: true - MaxNickLen: 15 - MenuMaxIdleTime: 600 - -Menus: - test: - MenuEntries: - - Key: q - Label: quit - Action: quit diff --git a/config/test_data/unreachable_game.yaml b/config/test_data/unreachable_game.yaml deleted file mode 100644 index f2f22e2..0000000 --- a/config/test_data/unreachable_game.yaml +++ /dev/null @@ -1,25 +0,0 @@ -App: - WorkingDirectory: var/ - MaxUsers: 1 - AllowRegistration: true - MaxNickLen: 15 - MenuMaxIdleTime: 600 - -Menus: - anonymous: - MenuEntries: - - Key: q - Label: quit - Action: quit - logged_in: - MenuEntries: - - Key: q - Label: quit - Action: quit - -Games: - unreachable: - ChrootPath: test_data/fake_nethack_directory - FileMode: 0777 - Commands: - - wait diff --git a/config/test_data/unreachable_menu.yaml b/config/test_data/unreachable_menu.yaml deleted file mode 100644 index f947cf7..0000000 --- a/config/test_data/unreachable_menu.yaml +++ /dev/null @@ -1,23 +0,0 @@ -App: - WorkingDirectory: var/ - MaxUsers: 1 - AllowRegistration: true - MaxNickLen: 15 - MenuMaxIdleTime: 600 - -Menus: - anonymous: - MenuEntries: - - Key: q - Label: quit - Action: quit - logged_in: - MenuEntries: - - Key: q - Label: quit - Action: quit - test: - MenuEntries: - - Key: q - Label: quit - Action: quit -- cgit v1.2.3