From bb38c90fdead8d1a9bfe254cb84b4a73ad8cfb71 Mon Sep 17 00:00:00 2001 From: ConfusedPolarBear <33811686+ConfusedPolarBear@users.noreply.github.com> Date: Thu, 1 Sep 2022 21:46:50 -0500 Subject: [PATCH] Automatically setup containers under test (cherry picked from commit fa9eba300afd1b85eca88b67930c1ab80ac2ec80) --- .../e2e_tests/config_sample.jsonc | 3 +- .../e2e_tests/verifier/report_generator.go | 23 ++++- .../e2e_tests/wrapper/library.json | 93 +++++++++++++++++++ .../e2e_tests/wrapper/main.go | 68 +++++++++++--- .../e2e_tests/wrapper/setup.go | 79 ++++++++++++++++ .../e2e_tests/wrapper/structs.go | 1 - 6 files changed, 246 insertions(+), 21 deletions(-) create mode 100644 ConfusedPolarBear.Plugin.IntroSkipper.Tests/e2e_tests/wrapper/library.json create mode 100644 ConfusedPolarBear.Plugin.IntroSkipper.Tests/e2e_tests/wrapper/setup.go diff --git a/ConfusedPolarBear.Plugin.IntroSkipper.Tests/e2e_tests/config_sample.jsonc b/ConfusedPolarBear.Plugin.IntroSkipper.Tests/e2e_tests/config_sample.jsonc index c2eedc8..a800145 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper.Tests/e2e_tests/config_sample.jsonc +++ b/ConfusedPolarBear.Plugin.IntroSkipper.Tests/e2e_tests/config_sample.jsonc @@ -1,6 +1,6 @@ { "common": { - "library": "/full/path/to/test/library/on/host", + "library": "/full/path/to/test/library/on/host/TV", "episode": "Episode title to search for" }, "servers": [ @@ -9,7 +9,6 @@ "image": "ghcr.io/confusedpolarbear/jellyfin-intro-skipper:latest", "username": "admin", "password": "hunter2", - "base": "config/official", "browsers": [ "chrome", "firefox" diff --git a/ConfusedPolarBear.Plugin.IntroSkipper.Tests/e2e_tests/verifier/report_generator.go b/ConfusedPolarBear.Plugin.IntroSkipper.Tests/e2e_tests/verifier/report_generator.go index d6f13b4..4814022 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper.Tests/e2e_tests/verifier/report_generator.go +++ b/ConfusedPolarBear.Plugin.IntroSkipper.Tests/e2e_tests/verifier/report_generator.go @@ -99,6 +99,8 @@ func generateReport(hostAddress, apiKey, reportDestination string, keepTimestamp } func runAnalysisAndWait(hostAddress, apiKey string, pollInterval time.Duration) { + var taskId string = "" + type taskInfo struct { State string CurrentProgressPercentage int @@ -108,9 +110,24 @@ func runAnalysisAndWait(hostAddress, apiKey string, pollInterval time.Duration) SendRequest("POST", hostAddress+"/Intros/EraseTimestamps", apiKey) fmt.Println() + // The task ID changed with v0.1.7. + // Old task ID: 8863329048cc357f7dfebf080f2fe204 + // New task ID: 6adda26c5261c40e8fa4a7e7df568be2 fmt.Println("[+] Starting analysis task") - SendRequest("POST", hostAddress+"/ScheduledTasks/Running/6adda26c5261c40e8fa4a7e7df568be2", apiKey) - fmt.Println() + for _, id := range []string{"8863329048cc357f7dfebf080f2fe204", "6adda26c5261c40e8fa4a7e7df568be2"} { + body := SendRequest("POST", hostAddress+"/ScheduledTasks/Running/"+id, apiKey) + fmt.Println() + + // If the scheduled task was found, store the task ID for later + if !strings.Contains(string(body), "Not Found") { + taskId = id + break + } + } + + if taskId == "" { + panic("unable to find scheduled task") + } fmt.Println("[+] Waiting for analysis task to complete") fmt.Print("[+] Episodes analyzed: 0%") @@ -141,7 +158,7 @@ func runAnalysisAndWait(hostAddress, apiKey string, pollInterval time.Duration) lastQuery = time.Now() - raw := SendRequest("GET", hostAddress+"/ScheduledTasks/6adda26c5261c40e8fa4a7e7df568be2?hideUrl=1", apiKey) + raw := SendRequest("GET", hostAddress+"/ScheduledTasks/"+taskId+"?hideUrl=1", apiKey) if err := json.Unmarshal(raw, &info); err != nil { fmt.Printf("[!] Unable to unmarshal response into taskInfo struct: %s\n", err) diff --git a/ConfusedPolarBear.Plugin.IntroSkipper.Tests/e2e_tests/wrapper/library.json b/ConfusedPolarBear.Plugin.IntroSkipper.Tests/e2e_tests/wrapper/library.json new file mode 100644 index 0000000..5c35999 --- /dev/null +++ b/ConfusedPolarBear.Plugin.IntroSkipper.Tests/e2e_tests/wrapper/library.json @@ -0,0 +1,93 @@ +{ + "LibraryOptions": { + "EnableArchiveMediaFiles": false, + "EnablePhotos": false, + "EnableRealtimeMonitor": false, + "ExtractChapterImagesDuringLibraryScan": false, + "EnableChapterImageExtraction": false, + "EnableInternetProviders": false, + "SaveLocalMetadata": false, + "EnableAutomaticSeriesGrouping": false, + "PreferredMetadataLanguage": "", + "MetadataCountryCode": "", + "SeasonZeroDisplayName": "Specials", + "AutomaticRefreshIntervalDays": 0, + "EnableEmbeddedTitles": false, + "EnableEmbeddedEpisodeInfos": false, + "AllowEmbeddedSubtitles": "AllowAll", + "SkipSubtitlesIfEmbeddedSubtitlesPresent": false, + "SkipSubtitlesIfAudioTrackMatches": false, + "SaveSubtitlesWithMedia": true, + "RequirePerfectSubtitleMatch": true, + "AutomaticallyAddToCollection": false, + "MetadataSavers": [], + "TypeOptions": [ + { + "Type": "Series", + "MetadataFetchers": [ + "TheMovieDb", + "The Open Movie Database" + ], + "MetadataFetcherOrder": [ + "TheMovieDb", + "The Open Movie Database" + ], + "ImageFetchers": [ + "TheMovieDb" + ], + "ImageFetcherOrder": [ + "TheMovieDb" + ] + }, + { + "Type": "Season", + "MetadataFetchers": [ + "TheMovieDb" + ], + "MetadataFetcherOrder": [ + "TheMovieDb" + ], + "ImageFetchers": [ + "TheMovieDb" + ], + "ImageFetcherOrder": [ + "TheMovieDb" + ] + }, + { + "Type": "Episode", + "MetadataFetchers": [ + "TheMovieDb", + "The Open Movie Database" + ], + "MetadataFetcherOrder": [ + "TheMovieDb", + "The Open Movie Database" + ], + "ImageFetchers": [ + "TheMovieDb", + "The Open Movie Database", + "Embedded Image Extractor", + "Screen Grabber" + ], + "ImageFetcherOrder": [ + "TheMovieDb", + "The Open Movie Database", + "Embedded Image Extractor", + "Screen Grabber" + ] + } + ], + "LocalMetadataReaderOrder": [ + "Nfo" + ], + "SubtitleDownloadLanguages": [], + "DisabledSubtitleFetchers": [], + "SubtitleFetcherOrder": [], + "PathInfos": [ + { + "Path": "/media/TV" + } + ] + } +} diff --git a/ConfusedPolarBear.Plugin.IntroSkipper.Tests/e2e_tests/wrapper/main.go b/ConfusedPolarBear.Plugin.IntroSkipper.Tests/e2e_tests/wrapper/main.go index b22e339..fe4826f 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper.Tests/e2e_tests/wrapper/main.go +++ b/ConfusedPolarBear.Plugin.IntroSkipper.Tests/e2e_tests/wrapper/main.go @@ -2,6 +2,8 @@ package main import ( "bytes" + "crypto/rand" + "encoding/hex" "encoding/json" "flag" "fmt" @@ -19,10 +21,21 @@ var containerAddress string // Path to compiled plugin DLL to install in local containers. var pluginPath string +// Randomly generated password used to setup container with. +var containerPassword string + func flags() { flag.StringVar(&pluginPath, "dll", "", "Path to plugin DLL to install in container images.") flag.StringVar(&containerAddress, "caddr", "", "IP address to use when connecting to local containers.") flag.Parse() + + // Randomize the container's password + rawPassword := make([]byte, 32) + if _, err := rand.Read(rawPassword); err != nil { + panic(err) + } + + containerPassword = hex.EncodeToString(rawPassword) } func main() { @@ -72,11 +85,6 @@ func main() { panic(err) } - // Copy the contents of the base configuration directory to a temp folder for the container - src, dst := server.Base+"/.", configurationDirectory - fmt.Printf(" [+] Copying %s to %s\n", src, dst) - RunProgram("cp", []string{"-ar", src, dst}, 10*time.Second) - // Create a folder to install the plugin into pluginDirectory := path.Join(configurationDirectory, "plugins", "intro-skipper") if lsioImage { @@ -91,15 +99,19 @@ func main() { // If this is an LSIO container, adjust the permissions on the plugin directory if lsioImage { - if err := os.Chown(pluginDirectory, 911, 911); err != nil { - fmt.Printf(" [!] Failed to change plugin directory UID/GID: %s\n", err) - goto cleanup - } + RunProgram( + "chown", + []string{ + "911:911", + "-R", + path.Join(configurationDirectory, "data", "plugins")}, + 2*time.Second) } // Install the plugin fmt.Printf(" [+] Copying plugin %s to %s\n", pluginPath, pluginDirectory) RunProgram("cp", []string{pluginPath, pluginDirectory}, 2*time.Second) + fmt.Println() /* Start the container with the following settings: * Name: jf-e2e @@ -116,6 +128,18 @@ func main() { // Wait for the container to fully start waitForServerStartup(server.Address) + fmt.Println() + + fmt.Println(" [+] Setting up container") + + // Set up the container + SetupServer(server.Address, containerPassword) + + // Restart the container and wait for it to come back up + RunProgram("docker", []string{"restart", "jf-e2e"}, 10*time.Second) + time.Sleep(time.Second) + waitForServerStartup(server.Address) + fmt.Println() } else { fmt.Println("[+] Remote instance, assuming plugin is already installed") } @@ -123,6 +147,21 @@ func main() { // Get an API key apiKey = login(server) + // Rescan the library if this is a server that we just setup + if server.Docker { + fmt.Println(" [+] Rescanning library") + + sendRequest( + server.Address+"/ScheduledTasks/Running/7738148ffcd07979c7ceb148e06b3aed?api_key="+apiKey, + "POST", + "") + + // TODO: poll for task completion + time.Sleep(10 * time.Second) + + fmt.Println() + } + // Analyze episodes and save report fmt.Println(" [+] Analyzing episodes") fmt.Print("\033[37;1m") // change the color of the verifier's text @@ -268,8 +307,9 @@ func loadConfiguration() Configuration { } // Print debugging info - fmt.Printf("Library: %s\n", config.Common.Library) - fmt.Printf("Episode: \"%s\"\n", config.Common.Episode) + fmt.Printf("Library: %s\n", config.Common.Library) + fmt.Printf("Episode: \"%s\"\n", config.Common.Episode) + fmt.Printf("Password: %s\n", containerPassword) fmt.Println() // Check the validity of all entries @@ -282,14 +322,12 @@ func loadConfiguration() Configuration { panic("The -caddr argument is required.") } - if server.Base == "" { - panic("Original configuration directory is required") - } - if pluginPath == "" { panic("The -dll argument is required.") } + server.Username = "admin" + server.Password = containerPassword server.Address = fmt.Sprintf("http://%s:8097", containerAddress) server.Docker = true } diff --git a/ConfusedPolarBear.Plugin.IntroSkipper.Tests/e2e_tests/wrapper/setup.go b/ConfusedPolarBear.Plugin.IntroSkipper.Tests/e2e_tests/wrapper/setup.go new file mode 100644 index 0000000..b428c6d --- /dev/null +++ b/ConfusedPolarBear.Plugin.IntroSkipper.Tests/e2e_tests/wrapper/setup.go @@ -0,0 +1,79 @@ +package main + +import ( + "bytes" + _ "embed" + "fmt" + "net/http" +) + +//go:embed library.json +var librarySetupPayload string + +func SetupServer(server, password string) { + makeUrl := func(u string) string { + return fmt.Sprintf("%s/%s", server, u) + } + + // Set the server language to English + sendRequest( + makeUrl("Startup/Configuration"), + "POST", + `{"UICulture":"en-US","MetadataCountryCode":"US","PreferredMetadataLanguage":"en"}`) + + // Get the first user + sendRequest(makeUrl("Startup/User"), "GET", "") + + // Create the first user + sendRequest( + makeUrl("Startup/User"), + "POST", + fmt.Sprintf(`{"Name":"admin","Password":"%s"}`, password)) + + // Create a TV library from the media at /media/TV. + sendRequest( + makeUrl("Library/VirtualFolders?collectionType=tvshows&refreshLibrary=false&name=Shows"), + "POST", + librarySetupPayload) + + // Setup remote access + sendRequest( + makeUrl("Startup/RemoteAccess"), + "POST", + `{"EnableRemoteAccess":true,"EnableAutomaticPortMapping":false}`) + + // Mark the wizard as complete + sendRequest( + makeUrl("Startup/Complete"), + "POST", + ``) +} + +func sendRequest(url string, method string, body string) { + // Create the request + req, err := http.NewRequest(method, url, bytes.NewBuffer([]byte(body))) + if err != nil { + panic(err) + } + + // Set required headers + req.Header.Set("Content-Type", "application/json") + req.Header.Set( + "X-Emby-Authorization", + `MediaBrowser Client="JF E2E Tests", Version="0.0.1", DeviceId="E2E", Device="E2E"`) + + // Send it + fmt.Printf(" [+] %s %s", method, url) + res, err := http.DefaultClient.Do(req) + + if err != nil { + fmt.Println() + panic(err) + } + + fmt.Printf(" %d\n", res.StatusCode) + + if res.StatusCode != http.StatusNoContent && res.StatusCode != http.StatusOK { + panic("invalid status code received during setup") + } +} diff --git a/ConfusedPolarBear.Plugin.IntroSkipper.Tests/e2e_tests/wrapper/structs.go b/ConfusedPolarBear.Plugin.IntroSkipper.Tests/e2e_tests/wrapper/structs.go index 206c1f3..9d472a5 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper.Tests/e2e_tests/wrapper/structs.go +++ b/ConfusedPolarBear.Plugin.IntroSkipper.Tests/e2e_tests/wrapper/structs.go @@ -17,7 +17,6 @@ type Server struct { Image string `json:"image"` Username string `json:"username"` Password string `json:"password"` - Base string `json:"base"` Browsers []string `json:"browsers"` Tests []string `json:"tests"`