package extension_repo import ( "archive/zip" "fmt" "net/http" "net/http/httptest" "os" "path/filepath" "seanime/internal/extension" "seanime/internal/plugin" "strings" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // TestGojaPluginSystemOS tests the $os bindings in the Goja plugin system func TestGojaPluginSystemOS(t *testing.T) { // Create a temporary directory for testing tempDir := t.TempDir() // Create test files and directories testFilePath := filepath.Join(tempDir, "test.txt") testDirPath := filepath.Join(tempDir, "testdir") testContent := []byte("Hello, world!") err := os.WriteFile(testFilePath, testContent, 0644) require.NoError(t, err) err = os.Mkdir(testDirPath, 0755) require.NoError(t, err) // Test $os.platform and $os.arch payload := ` function init() { $ui.register((ctx) => { console.log("Testing $os bindings"); // Test platform and arch console.log("Platform:", $os.platform); console.log("Arch:", $os.arch); // Test tempDir const tempDirPath = $os.tempDir(); console.log("Temp dir:", tempDirPath); // Test readFile const content = $os.readFile("${TEST_FILE_PATH}"); console.log("File content:", $toString(content)); $store.set("fileContent", $toString(content)); // Test writeFile $os.writeFile("${TEST_FILE_PATH}.new", $toBytes("New content"), 0644); const newContent = $os.readFile("${TEST_FILE_PATH}.new"); console.log("New file content:", $toString(newContent)); $store.set("newFileContent", $toString(newContent)); // Test readDir const entries = $os.readDir("${TEST_DIR}"); console.log("Directory entries:"); for (const entry of entries) { console.log(" Entry:", entry.name()); } $store.set("dirEntries", entries.length); // Test mkdir $os.mkdir("${TEST_DIR}/newdir", 0755); const newEntries = $os.readDir("${TEST_DIR}"); console.log("New directory entries:"); for (const entry of newEntries) { console.log(" Entry:", entry.name()); } $store.set("newDirEntries", newEntries.length); // Test stat const stats = $os.stat("${TEST_FILE_PATH}"); console.log("File stats:", stats); $store.set("fileSize", stats.size()); // Test rename $os.rename("${TEST_FILE_PATH}.new", "${TEST_FILE_PATH}.renamed"); const renamedExists = $os.stat("${TEST_FILE_PATH}.renamed") !== null; console.log("Renamed file exists:", renamedExists); $store.set("renamedExists", renamedExists); // Test remove $os.remove("${TEST_FILE_PATH}.renamed"); let removeSuccess = true; try { $os.stat("${TEST_FILE_PATH}.renamed"); removeSuccess = false; } catch (e) { // File should not exist removeSuccess = true; } console.log("Remove success:", removeSuccess); $store.set("removeSuccess", removeSuccess); }); } ` // Replace placeholders with actual paths payload = strings.ReplaceAll(payload, "${TEST_FILE_PATH}", testFilePath) payload = strings.ReplaceAll(payload, "${TEST_DIR}", tempDir) opts := DefaultTestPluginOptions() opts.Payload = payload opts.Permissions = extension.PluginPermissions{ Scopes: []extension.PluginPermissionScope{ extension.PluginPermissionSystem, }, Allow: extension.PluginAllowlist{ ReadPaths: []string{tempDir + "/**/*"}, WritePaths: []string{tempDir + "/**/*"}, }, } plugin, _, manager, _, _, err := InitTestPlugin(t, opts) require.NoError(t, err) // Wait for the plugin to execute time.Sleep(1 * time.Second) // Check the store values fileContent, ok := plugin.store.GetOk("fileContent") require.True(t, ok, "fileContent should be set in store") assert.Equal(t, "Hello, world!", fileContent) newFileContent, ok := plugin.store.GetOk("newFileContent") require.True(t, ok, "newFileContent should be set in store") assert.Equal(t, "New content", newFileContent) dirEntries, ok := plugin.store.GetOk("dirEntries") require.True(t, ok, "dirEntries should be set in store") assert.Equal(t, int64(3), dirEntries) // test.txt, test.txt.new and testdir newDirEntries, ok := plugin.store.GetOk("newDirEntries") require.True(t, ok, "newDirEntries should be set in store") assert.Equal(t, int64(4), newDirEntries) // test.txt, test.txt.new, testdir, and newdir fileSize, ok := plugin.store.GetOk("fileSize") require.True(t, ok, "fileSize should be set in store") assert.Equal(t, int64(13), fileSize) // "Hello, world!" is 13 bytes renamedExists, ok := plugin.store.GetOk("renamedExists") require.True(t, ok, "renamedExists should be set in store") assert.True(t, renamedExists.(bool)) removeSuccess, ok := plugin.store.GetOk("removeSuccess") require.True(t, ok, "removeSuccess should be set in store") assert.True(t, removeSuccess.(bool)) manager.PrintPluginPoolMetrics(opts.ID) } // TestGojaPluginSystemOSUnauthorized tests that unauthorized paths are rejected func TestGojaPluginSystemOSUnauthorized(t *testing.T) { // Create a temporary directory for testing tempDir := t.TempDir() unauthorizedDir := filepath.Join(os.TempDir(), "unauthorized") // Ensure the unauthorized directory exists _ = os.MkdirAll(unauthorizedDir, 0755) defer os.RemoveAll(unauthorizedDir) payload := ` function init() { $ui.register((ctx) => { console.log("Testing unauthorized $os operations"); // Try to read from unauthorized path try { const content = $os.readFile("${UNAUTHORIZED_PATH}/test.txt"); $store.set("unauthorizedRead", false); } catch (e) { console.log("Unauthorized read error:", e.message); $store.set("unauthorizedRead", true); $store.set("unauthorizedReadError", e.message); } // Try to write to unauthorized path try { $os.writeFile("${UNAUTHORIZED_PATH}/test.txt", $toBytes("Unauthorized"), 0644); $store.set("unauthorizedWrite", false); } catch (e) { console.log("Unauthorized write error:", e.message); $store.set("unauthorizedWrite", true); $store.set("unauthorizedWriteError", e.message); } // Try to read directory from unauthorized path try { const entries = $os.readDir("${UNAUTHORIZED_PATH}"); $store.set("unauthorizedReadDir", false); } catch (e) { console.log("Unauthorized readDir error:", e.message); $store.set("unauthorizedReadDir", true); $store.set("unauthorizedReadDirError", e.message); } }); } ` // Replace placeholders with actual paths payload = strings.ReplaceAll(payload, "${UNAUTHORIZED_PATH}", unauthorizedDir) opts := DefaultTestPluginOptions() opts.Payload = payload opts.Permissions = extension.PluginPermissions{ Scopes: []extension.PluginPermissionScope{ extension.PluginPermissionSystem, }, Allow: extension.PluginAllowlist{ ReadPaths: []string{tempDir + "/*"}, WritePaths: []string{tempDir + "/*"}, }, } plugin, _, manager, _, _, err := InitTestPlugin(t, opts) require.NoError(t, err) // Wait for the plugin to execute time.Sleep(1 * time.Second) // Check that unauthorized operations were rejected unauthorizedRead, ok := plugin.store.GetOk("unauthorizedRead") require.True(t, ok, "unauthorizedRead should be set in store") assert.True(t, unauthorizedRead.(bool)) unauthorizedReadError, ok := plugin.store.GetOk("unauthorizedReadError") require.True(t, ok, "unauthorizedReadError should be set in store") assert.Contains(t, unauthorizedReadError.(string), "not authorized for read") unauthorizedWrite, ok := plugin.store.GetOk("unauthorizedWrite") require.True(t, ok, "unauthorizedWrite should be set in store") assert.True(t, unauthorizedWrite.(bool)) unauthorizedWriteError, ok := plugin.store.GetOk("unauthorizedWriteError") require.True(t, ok, "unauthorizedWriteError should be set in store") assert.Contains(t, unauthorizedWriteError.(string), "not authorized for write") unauthorizedReadDir, ok := plugin.store.GetOk("unauthorizedReadDir") require.True(t, ok, "unauthorizedReadDir should be set in store") assert.True(t, unauthorizedReadDir.(bool)) unauthorizedReadDirError, ok := plugin.store.GetOk("unauthorizedReadDirError") require.True(t, ok, "unauthorizedReadDirError should be set in store") assert.Contains(t, unauthorizedReadDirError.(string), "not authorized for read") manager.PrintPluginPoolMetrics(opts.ID) } // TestGojaPluginSystemOSOpenFile tests the $os.openFile, $os.create functions func TestGojaPluginSystemOSOpenFile(t *testing.T) { // Create a temporary directory for testing tempDir := t.TempDir() payload := ` function init() { $ui.register((ctx) => { console.log("Testing $os.openFile and $os.create"); // Test create const file = $os.create("${TEMP_DIR}/created.txt"); file.writeString("Created file content"); file.close(); const createdContent = $os.readFile("${TEMP_DIR}/created.txt"); console.log("Created file content:", $toString(createdContent)); $store.set("createdContent", $toString(createdContent)); // Test openFile for reading and writing const fileRW = $os.openFile("${TEMP_DIR}/rw.txt", $os.O_RDWR | $os.O_CREATE, 0644); fileRW.writeString("Read-write file content"); fileRW.close(); const fileRead = $os.openFile("${TEMP_DIR}/rw.txt", $os.O_RDONLY, 0644); const buffer = new Uint8Array(100); const bytesRead = fileRead.read(buffer); const content = $toString(buffer.subarray(0, bytesRead)); console.log("Read-write file content:", content); $store.set("rwContent", content); fileRead.close(); // Test openFile with append const fileAppend = $os.openFile("${TEMP_DIR}/rw.txt", $os.O_WRONLY | $os.O_APPEND, 0644); fileAppend.writeString(" - Appended content"); fileAppend.close(); const appendedContent = $os.readFile("${TEMP_DIR}/rw.txt"); console.log("Appended file content:", $toString(appendedContent)); $store.set("appendedContent", $toString(appendedContent)); }); } ` // Replace placeholders with actual paths payload = strings.ReplaceAll(payload, "${TEMP_DIR}", tempDir) opts := DefaultTestPluginOptions() opts.Payload = payload opts.Permissions = extension.PluginPermissions{ Scopes: []extension.PluginPermissionScope{ extension.PluginPermissionSystem, }, Allow: extension.PluginAllowlist{ ReadPaths: []string{tempDir + "/*"}, WritePaths: []string{tempDir + "/*"}, }, } plugin, _, manager, _, _, err := InitTestPlugin(t, opts) require.NoError(t, err) // Wait for the plugin to execute time.Sleep(1 * time.Second) // Check the store values createdContent, ok := plugin.store.GetOk("createdContent") require.True(t, ok, "createdContent should be set in store") assert.Equal(t, "Created file content", createdContent) rwContent, ok := plugin.store.GetOk("rwContent") require.True(t, ok, "rwContent should be set in store") assert.Equal(t, "Read-write file content", rwContent) appendedContent, ok := plugin.store.GetOk("appendedContent") require.True(t, ok, "appendedContent should be set in store") assert.Equal(t, "Read-write file content - Appended content", appendedContent) manager.PrintPluginPoolMetrics(opts.ID) } // TestGojaPluginSystemOSMkdirAll tests the $os.mkdirAll function func TestGojaPluginSystemOSMkdirAll(t *testing.T) { // Create a temporary directory for testing tempDir := t.TempDir() payload := ` function init() { $ui.register((ctx) => { console.log("Testing $os.mkdirAll"); // Test mkdirAll with nested directories $os.mkdirAll("${TEMP_DIR}/nested/dirs/structure", 0755); // Check if the directories were created const nestedExists = $os.stat("${TEMP_DIR}/nested") !== null; const dirsExists = $os.stat("${TEMP_DIR}/nested/dirs") !== null; const structureExists = $os.stat("${TEMP_DIR}/nested/dirs/structure") !== null; console.log("Nested directories exist:", nestedExists, dirsExists, structureExists); $store.set("nestedExists", nestedExists); $store.set("dirsExists", dirsExists); $store.set("structureExists", structureExists); // Create a file in the nested directory $os.writeFile("${TEMP_DIR}/nested/dirs/structure/test.txt", $toBytes("Nested file"), 0644); const nestedContent = $os.readFile("${TEMP_DIR}/nested/dirs/structure/test.txt"); console.log("Nested file content:", $toString(nestedContent)); $store.set("nestedContent", $toString(nestedContent)); // Test removeAll $os.removeAll("${TEMP_DIR}/nested"); let removeAllSuccess = true; try { $os.stat("${TEMP_DIR}/nested"); removeAllSuccess = false; } catch (e) { // Directory should not exist removeAllSuccess = true; } console.log("RemoveAll success:", removeAllSuccess); $store.set("removeAllSuccess", removeAllSuccess); }); } ` // Replace placeholders with actual paths payload = strings.ReplaceAll(payload, "${TEMP_DIR}", tempDir) opts := DefaultTestPluginOptions() opts.Payload = payload opts.Permissions = extension.PluginPermissions{ Scopes: []extension.PluginPermissionScope{ extension.PluginPermissionSystem, }, Allow: extension.PluginAllowlist{ ReadPaths: []string{tempDir + "/**/*"}, WritePaths: []string{tempDir + "/**/*"}, }, } plugin, _, manager, _, _, err := InitTestPlugin(t, opts) require.NoError(t, err) // Wait for the plugin to execute time.Sleep(1 * time.Second) // Check the store values nestedExists, ok := plugin.store.GetOk("nestedExists") require.True(t, ok, "nestedExists should be set in store") assert.True(t, nestedExists.(bool)) dirsExists, ok := plugin.store.GetOk("dirsExists") require.True(t, ok, "dirsExists should be set in store") assert.True(t, dirsExists.(bool)) structureExists, ok := plugin.store.GetOk("structureExists") require.True(t, ok, "structureExists should be set in store") assert.True(t, structureExists.(bool)) nestedContent, ok := plugin.store.GetOk("nestedContent") require.True(t, ok, "nestedContent should be set in store") assert.Equal(t, "Nested file", nestedContent) removeAllSuccess, ok := plugin.store.GetOk("removeAllSuccess") require.True(t, ok, "removeAllSuccess should be set in store") assert.True(t, removeAllSuccess.(bool)) manager.PrintPluginPoolMetrics(opts.ID) } // TestGojaPluginSystemOSPermissions tests that the plugin system enforces permissions correctly func TestGojaPluginSystemOSPermissions(t *testing.T) { payload := ` function init() { $ui.register((ctx) => { console.log("Testing $os permissions"); // Try to use $os without system permission try { const tempDirPath = $os.tempDir(); $store.set("noPermissionAccess", true); } catch (e) { console.log("No permission error:", e.message); $store.set("noPermissionAccess", false); $store.set("noPermissionError", e.message); } }); } ` opts := DefaultTestPluginOptions() opts.Payload = payload // Deliberately NOT including the system permission opts.Permissions = extension.PluginPermissions{ Scopes: []extension.PluginPermissionScope{}, } plugin, _, manager, _, _, err := InitTestPlugin(t, opts) require.NoError(t, err) // Wait for the plugin to execute time.Sleep(1 * time.Second) // Check that operations were rejected due to missing permissions noPermissionAccess, ok := plugin.store.GetOk("noPermissionAccess") require.True(t, ok, "noPermissionAccess should be set in store") assert.False(t, noPermissionAccess.(bool)) noPermissionError, ok := plugin.store.GetOk("noPermissionError") require.True(t, ok, "noPermissionError should be set in store") assert.Contains(t, noPermissionError.(string), "$os is not defined") manager.PrintPluginPoolMetrics(opts.ID) } // TestGojaPluginSystemFilepath tests the $filepath bindings in the Goja plugin system func TestGojaPluginSystemFilepath(t *testing.T) { // Create a temporary directory for testing tempDir := t.TempDir() // Create test files and directories testFilePath := filepath.Join(tempDir, "test.txt") nestedDir := filepath.Join(tempDir, "nested", "dir") err := os.MkdirAll(nestedDir, 0755) require.NoError(t, err) err = os.WriteFile(testFilePath, []byte("Hello, world!"), 0644) require.NoError(t, err) payload := ` function init() { $ui.register((ctx) => { console.log("Testing $filepath bindings"); // Test base const baseName = $filepath.base("${TEST_FILE_PATH}"); console.log("Base name:", baseName); $store.set("baseName", baseName); // Test dir const dirName = $filepath.dir("${TEST_FILE_PATH}"); console.log("Dir name:", dirName); $store.set("dirName", dirName); // Test ext const extName = $filepath.ext("${TEST_FILE_PATH}"); console.log("Ext name:", extName); $store.set("extName", extName); // Test join const joinedPath = $filepath.join("${TEMP_DIR}", "subdir", "file.txt"); console.log("Joined path:", joinedPath); $store.set("joinedPath", joinedPath); // Test split const [dir, file] = $filepath.split("${TEST_FILE_PATH}"); console.log("Split path:", dir, file); $store.set("splitDir", dir); $store.set("splitFile", file); // Test glob const globResults = $filepath.glob("${TEMP_DIR}", "*.txt"); console.log("Glob results:", globResults); $store.set("globResults", globResults.length); // Test match const isMatch = $filepath.match("*.txt", "test.txt"); console.log("Match result:", isMatch); $store.set("isMatch", isMatch); // Test isAbs const isAbsPath = $filepath.isAbs("${TEST_FILE_PATH}"); console.log("Is absolute path:", isAbsPath); $store.set("isAbsPath", isAbsPath); // Test toSlash and fromSlash const slashPath = $filepath.toSlash("${TEST_FILE_PATH}"); console.log("To slash:", slashPath); $store.set("slashPath", slashPath); const fromSlashPath = $filepath.fromSlash(slashPath); console.log("From slash:", fromSlashPath); $store.set("fromSlashPath", fromSlashPath); }); } ` // Replace placeholders with actual paths payload = strings.ReplaceAll(payload, "${TEST_FILE_PATH}", testFilePath) payload = strings.ReplaceAll(payload, "${TEMP_DIR}", tempDir) opts := DefaultTestPluginOptions() opts.Payload = payload opts.Permissions = extension.PluginPermissions{ Scopes: []extension.PluginPermissionScope{ extension.PluginPermissionSystem, }, Allow: extension.PluginAllowlist{ ReadPaths: []string{tempDir + "/**/*"}, WritePaths: []string{tempDir + "/**/*"}, }, } plugin, _, manager, _, _, err := InitTestPlugin(t, opts) require.NoError(t, err) // Wait for the plugin to execute time.Sleep(1 * time.Second) // Check the store values baseName, ok := plugin.store.GetOk("baseName") require.True(t, ok, "baseName should be set in store") assert.Equal(t, "test.txt", baseName) dirName, ok := plugin.store.GetOk("dirName") require.True(t, ok, "dirName should be set in store") assert.Equal(t, tempDir, dirName) extName, ok := plugin.store.GetOk("extName") require.True(t, ok, "extName should be set in store") assert.Equal(t, ".txt", extName) joinedPath, ok := plugin.store.GetOk("joinedPath") require.True(t, ok, "joinedPath should be set in store") assert.Equal(t, filepath.Join(tempDir, "subdir", "file.txt"), joinedPath) splitDir, ok := plugin.store.GetOk("splitDir") require.True(t, ok, "splitDir should be set in store") assert.Equal(t, tempDir+string(filepath.Separator), splitDir) splitFile, ok := plugin.store.GetOk("splitFile") require.True(t, ok, "splitFile should be set in store") assert.Equal(t, "test.txt", splitFile) globResults, ok := plugin.store.GetOk("globResults") require.True(t, ok, "globResults should be set in store") assert.Equal(t, int64(1), globResults) // test.txt isMatch, ok := plugin.store.GetOk("isMatch") require.True(t, ok, "isMatch should be set in store") assert.True(t, isMatch.(bool)) isAbsPath, ok := plugin.store.GetOk("isAbsPath") require.True(t, ok, "isAbsPath should be set in store") assert.True(t, isAbsPath.(bool)) slashPath, ok := plugin.store.GetOk("slashPath") require.True(t, ok, "slashPath should be set in store") assert.Contains(t, slashPath.(string), "/") fromSlashPath, ok := plugin.store.GetOk("fromSlashPath") require.True(t, ok, "fromSlashPath should be set in store") assert.Equal(t, testFilePath, fromSlashPath) manager.PrintPluginPoolMetrics(opts.ID) } // TestGojaPluginSystemIO tests the $io bindings in the Goja plugin system func TestGojaPluginSystemIO(t *testing.T) { // Create a temporary directory for testing tempDir := t.TempDir() // Create test file testFilePath := filepath.Join(tempDir, "test.txt") err := os.WriteFile(testFilePath, []byte("Hello, world!"), 0644) require.NoError(t, err) payload := ` function init() { $ui.register((ctx) => { console.log("Testing $io bindings"); // Test readAll const file = $os.openFile("${TEST_FILE_PATH}", $os.O_RDONLY, 0); const content = $io.readAll(file); file.close(); console.log("Read content:", $toString(content)); $store.set("readAllContent", $toString(content)); // Test copy const srcFile = $os.openFile("${TEST_FILE_PATH}", $os.O_RDONLY, 0); const destFile = $os.create("${TEMP_DIR}/copy.txt"); const bytesCopied = $io.copy(destFile, srcFile); srcFile.close(); destFile.close(); console.log("Bytes copied:", bytesCopied); $store.set("bytesCopied", bytesCopied); // Test writeString const stringFile = $os.create("${TEMP_DIR}/string.txt"); const bytesWritten = $io.writeString(stringFile, "Written with writeString"); stringFile.close(); console.log("Bytes written:", bytesWritten); $store.set("bytesWritten", bytesWritten); // Read the file back to verify const stringContent = $os.readFile("${TEMP_DIR}/string.txt"); console.log("String content:", $toString(stringContent)); $store.set("stringContent", $toString(stringContent)); // Test copyN const srcFileN = $os.openFile("${TEST_FILE_PATH}", $os.O_RDONLY, 0); const destFileN = $os.create("${TEMP_DIR}/copyN.txt"); const bytesCopiedN = $io.copyN(destFileN, srcFileN, 5); // Copy only 5 bytes srcFileN.close(); destFileN.close(); console.log("Bytes copied with copyN:", bytesCopiedN); $store.set("bytesCopiedN", bytesCopiedN); // Read the file back to verify const copyNContent = $os.readFile("${TEMP_DIR}/copyN.txt"); console.log("CopyN content:", $toString(copyNContent)); $store.set("copyNContent", $toString(copyNContent)); // Test limitReader const bigFile = $os.openFile("${TEST_FILE_PATH}", $os.O_RDONLY, 0); const limitedReader = $io.limitReader(bigFile, 5); // Limit to 5 bytes const limitBuffer = new Uint8Array(100); const limitBytesRead = limitedReader.read(limitBuffer); bigFile.close(); console.log("Limited bytes read:", limitBytesRead); $store.set("limitBytesRead", limitBytesRead); $store.set("limitContent", $toString(limitBuffer.subarray(0, limitBytesRead))); }); } ` // Replace placeholders with actual paths payload = strings.ReplaceAll(payload, "${TEST_FILE_PATH}", testFilePath) payload = strings.ReplaceAll(payload, "${TEMP_DIR}", tempDir) opts := DefaultTestPluginOptions() opts.Payload = payload opts.Permissions = extension.PluginPermissions{ Scopes: []extension.PluginPermissionScope{ extension.PluginPermissionSystem, }, Allow: extension.PluginAllowlist{ ReadPaths: []string{tempDir + "/**/*"}, WritePaths: []string{tempDir + "/**/*"}, }, } plugin, _, manager, _, _, err := InitTestPlugin(t, opts) require.NoError(t, err) // Wait for the plugin to execute time.Sleep(1 * time.Second) // Check the store values readAllContent, ok := plugin.store.GetOk("readAllContent") require.True(t, ok, "readAllContent should be set in store") assert.Equal(t, "Hello, world!", readAllContent) bytesCopied, ok := plugin.store.GetOk("bytesCopied") require.True(t, ok, "bytesCopied should be set in store") assert.Equal(t, int64(13), bytesCopied) // "Hello, world!" is 13 bytes bytesWritten, ok := plugin.store.GetOk("bytesWritten") require.True(t, ok, "bytesWritten should be set in store") assert.Equal(t, int64(24), bytesWritten) // "Written with writeString" is 24 bytes stringContent, ok := plugin.store.GetOk("stringContent") require.True(t, ok, "stringContent should be set in store") assert.Equal(t, "Written with writeString", stringContent) bytesCopiedN, ok := plugin.store.GetOk("bytesCopiedN") require.True(t, ok, "bytesCopiedN should be set in store") assert.Equal(t, int64(5), bytesCopiedN) copyNContent, ok := plugin.store.GetOk("copyNContent") require.True(t, ok, "copyNContent should be set in store") assert.Equal(t, "Hello", copyNContent) limitBytesRead, ok := plugin.store.GetOk("limitBytesRead") require.True(t, ok, "limitBytesRead should be set in store") assert.Equal(t, int64(5), limitBytesRead) limitContent, ok := plugin.store.GetOk("limitContent") require.True(t, ok, "limitContent should be set in store") assert.Equal(t, "Hello", limitContent) manager.PrintPluginPoolMetrics(opts.ID) } // TestGojaPluginSystemBufio tests the $bufio bindings in the Goja plugin system func TestGojaPluginSystemBufio(t *testing.T) { // Create a temporary directory for testing tempDir := t.TempDir() // Create test file with multiple lines testFilePath := filepath.Join(tempDir, "multiline.txt") err := os.WriteFile(testFilePath, []byte("Line 1\nLine 2\nLine 3\nLine 4\n"), 0644) require.NoError(t, err) payload := ` function init() { $ui.register((ctx) => { console.log("Testing $bufio bindings"); // Test NewReader and ReadString const file = $os.openFile("${TEST_FILE_PATH}", $os.O_RDONLY, 0); const reader = $bufio.newReader(file); // Read lines one by one with try/catch to handle EOF const lines = []; for (let i = 0; i < 10; i++) { // Try to read more lines than exist try { const line = reader.readString($toBytes('\n')); console.log("Read line:", line); lines.push(line.trim()); } catch (e) { console.log("Caught expected EOF:", e.message); $store.set("eofCaught", true); } } file.close(); console.log("Read lines:", lines); $store.set("lines", lines); // Test NewWriter const writeFile = $os.create("${TEMP_DIR}/bufio_write.txt"); const writer = $bufio.newWriter(writeFile); // Write multiple strings writer.writeString("Buffered "); writer.writeString("write "); writer.writeString("test"); // Flush to ensure data is written writer.flush(); writeFile.close(); // Read back the file to verify const writtenContent = $os.readFile("${TEMP_DIR}/bufio_write.txt"); console.log("Written content:", $toString(writtenContent)); $store.set("writtenContent", $toString(writtenContent)); // Test Scanner const scanFile = $os.openFile("${TEST_FILE_PATH}", $os.O_RDONLY, 0); const scanner = $bufio.newScanner(scanFile); // Scan lines const scannedLines = []; while (scanner.scan()) { scannedLines.push(scanner.text()); } scanFile.close(); console.log("Scanned lines:", scannedLines); $store.set("scannedLines", scannedLines); // Test ReadBytes try { const bytesFile = $os.openFile("${TEST_FILE_PATH}", $os.O_RDONLY, 0); const bytesReader = $bufio.newReader(bytesFile); const bytesLines = []; try { for (let i = 0; i < 10; i++) { const lineBytes = bytesReader.readBytes('\n'.charCodeAt(0)); bytesLines.push($toString(lineBytes).trim()); } } catch (e) { console.log("Caught expected EOF in readBytes:", e.message); $store.set("eofCaughtBytes", true); } bytesFile.close(); console.log("Read bytes lines:", bytesLines); $store.set("bytesLines", bytesLines); } catch (e) { console.log("Error in ReadBytes test:", e.message); } }); } ` // Replace placeholders with actual paths payload = strings.ReplaceAll(payload, "${TEST_FILE_PATH}", testFilePath) payload = strings.ReplaceAll(payload, "${TEMP_DIR}", tempDir) opts := DefaultTestPluginOptions() opts.Payload = payload opts.Permissions = extension.PluginPermissions{ Scopes: []extension.PluginPermissionScope{ extension.PluginPermissionSystem, }, Allow: extension.PluginAllowlist{ ReadPaths: []string{tempDir + "/**/*"}, WritePaths: []string{tempDir + "/**/*"}, }, } plugin, _, manager, _, _, err := InitTestPlugin(t, opts) require.NoError(t, err) // Wait for the plugin to execute time.Sleep(1 * time.Second) // Check the store values lines, ok := plugin.store.GetOk("lines") require.True(t, ok, "lines should be set in store") assert.Equal(t, []interface{}{"Line 1", "Line 2", "Line 3", "Line 4"}, lines) eofCaught, ok := plugin.store.GetOk("eofCaught") require.True(t, ok, "eofCaught should be set in store") assert.True(t, eofCaught.(bool)) writtenContent, ok := plugin.store.GetOk("writtenContent") require.True(t, ok, "writtenContent should be set in store") assert.Equal(t, "Buffered write test", writtenContent) scannedLines, ok := plugin.store.GetOk("scannedLines") require.True(t, ok, "scannedLines should be set in store") assert.Equal(t, []interface{}{"Line 1", "Line 2", "Line 3", "Line 4"}, scannedLines) bytesLines, ok := plugin.store.GetOk("bytesLines") if ok { assert.Equal(t, []interface{}{"Line 1", "Line 2", "Line 3", "Line 4"}, bytesLines) } eofCaughtBytes, ok := plugin.store.GetOk("eofCaughtBytes") if ok { assert.True(t, eofCaughtBytes.(bool)) } manager.PrintPluginPoolMetrics(opts.ID) } // TestGojaPluginSystemBytes tests the $bytes bindings in the Goja plugin system func TestGojaPluginSystemBytes(t *testing.T) { // Create a temporary directory for testing tempDir := t.TempDir() payload := ` function init() { $ui.register((ctx) => { console.log("Testing $bytes bindings"); // Test NewBuffer const buffer = $bytes.newBuffer($toBytes("Hello")); buffer.writeString(", world!"); const bufferContent = $toString(buffer.bytes()); console.log("Buffer content:", bufferContent); $store.set("bufferContent", bufferContent); // Test NewBufferString const strBuffer = $bytes.newBufferString("String buffer"); strBuffer.writeString(" test"); const strBufferContent = strBuffer.string(); console.log("String buffer content:", strBufferContent); $store.set("strBufferContent", strBufferContent); // Test NewReader const reader = $bytes.newReader($toBytes("Bytes reader test")); const readerBuffer = new Uint8Array(100); const bytesRead = reader.read(readerBuffer); const readerContent = $toString(readerBuffer.subarray(0, bytesRead)); console.log("Reader content:", readerContent); $store.set("readerContent", readerContent); // Test buffer methods const testBuffer = $bytes.newBuffer($toBytes("")); testBuffer.writeString("Test"); testBuffer.writeByte(32); // Space testBuffer.writeString("methods"); const testBufferContent = testBuffer.string(); console.log("Test buffer content:", testBufferContent); $store.set("testBufferContent", testBufferContent); // Test read methods const readBuffer = $bytes.newBuffer($toBytes("Read test")); const readByte = readBuffer.readByte(); console.log("Read byte:", String.fromCharCode(readByte)); $store.set("readByte", readByte); const nextBytes = new Uint8Array(4); readBuffer.read(nextBytes); console.log("Next bytes:", $toString(nextBytes)); $store.set("nextBytes", $toString(nextBytes)); }); } ` opts := DefaultTestPluginOptions() opts.Payload = payload opts.Permissions = extension.PluginPermissions{ Scopes: []extension.PluginPermissionScope{ extension.PluginPermissionSystem, }, Allow: extension.PluginAllowlist{ ReadPaths: []string{tempDir + "/**/*"}, WritePaths: []string{tempDir + "/**/*"}, }, } plugin, _, manager, _, _, err := InitTestPlugin(t, opts) require.NoError(t, err) // Wait for the plugin to execute time.Sleep(1 * time.Second) // Check the store values bufferContent, ok := plugin.store.GetOk("bufferContent") require.True(t, ok, "bufferContent should be set in store") assert.Equal(t, "Hello, world!", bufferContent) strBufferContent, ok := plugin.store.GetOk("strBufferContent") require.True(t, ok, "strBufferContent should be set in store") assert.Equal(t, "String buffer test", strBufferContent) readerContent, ok := plugin.store.GetOk("readerContent") require.True(t, ok, "readerContent should be set in store") assert.Equal(t, "Bytes reader test", readerContent) testBufferContent, ok := plugin.store.GetOk("testBufferContent") require.True(t, ok, "testBufferContent should be set in store") assert.Equal(t, "Test methods", testBufferContent) readByte, ok := plugin.store.GetOk("readByte") require.True(t, ok, "readByte should be set in store") assert.Equal(t, int64('R'), readByte) nextBytes, ok := plugin.store.GetOk("nextBytes") require.True(t, ok, "nextBytes should be set in store") assert.Equal(t, "ead ", nextBytes) manager.PrintPluginPoolMetrics(opts.ID) } // TestGojaPluginSystemDownloader tests the ctx.downloader bindings in the Goja plugin system func TestGojaPluginSystemDownloader(t *testing.T) { // Create a temporary directory for testing tempDir := t.TempDir() // Create a test HTTP server that serves a large file in chunks to simulate download progress const totalSize = 1024 * 1024 // 1MB server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Set content length for proper progress calculation w.Header().Set("Content-Length", fmt.Sprintf("%d", totalSize)) // Flush headers to client if f, ok := w.(http.Flusher); ok { f.Flush() } // Send data in chunks with delays to simulate download progress chunkSize := 32 * 1024 // 32KB chunks chunk := make([]byte, chunkSize) for i := 0; i < len(chunk); i++ { chunk[i] = byte(i % 256) } for sent := 0; sent < totalSize; sent += chunkSize { // Sleep to simulate network delay time.Sleep(100 * time.Millisecond) // Calculate remaining bytes remaining := totalSize - sent if remaining < chunkSize { chunkSize = remaining } // Write chunk w.Write(chunk[:chunkSize]) // Flush to ensure client receives data immediately if f, ok := w.(http.Flusher); ok { f.Flush() } } })) defer server.Close() payload := ` function init() { $ui.register((ctx) => { console.log("Testing ctx.downloader bindings with large file"); // Test download const downloadPath = "${TEMP_DIR}/large_download.bin"; try { const downloadID = ctx.downloader.download("${SERVER_URL}", downloadPath, { timeout: 60 // 60 second timeout }); console.log("Download started with ID:", downloadID); $store.set("downloadID", downloadID); // Track progress updates const progressUpdates = []; // Wait for download to complete let downloadComplete = ctx.state(false); const cancelWatch = ctx.downloader.watch(downloadID, (progress) => { // Store progress update progressUpdates.push({ percentage: progress.percentage, totalBytes: progress.totalBytes, speed: progress.speed, status: progress.status }); console.log("Download progress:", progress.percentage.toFixed(2), "%, ", "Speed:", (progress.speed / 1024).toFixed(2), "KB/s, ", "Downloaded:", (progress.totalBytes / 1024).toFixed(2), "KB" , progress); if (progress.status === "completed") { downloadComplete.set(true); $store.set("downloadComplete", true); $store.set("downloadProgress", progress); $store.set("progressUpdates", progressUpdates); } else if (progress.status === "error") { console.log("Download error:", progress.error); $store.set("downloadError", progress.error); } }); // Wait for download to complete ctx.effect(() => { if (!downloadComplete.get()) { return } // Cancel watch cancelWatch(); // Check downloaded file try { if (downloadComplete) { const stats = $os.stat(downloadPath); console.log("Downloaded file size:", stats.size(), "bytes"); $store.set("downloadedSize", stats.size()); } // List downloads const downloads = ctx.downloader.listDownloads(); console.log("Active downloads:", downloads.length); $store.set("downloadsCount", downloads.length); // Get progress const progress = ctx.downloader.getProgress(downloadID); if (progress) { console.log("Final download progress:", progress); $store.set("finalProgress", progress); } else { console.log("Progress not found for ID:", downloadID); $store.set("progressNotFound", true); } } catch (e) { console.log("Error in download check:", e.message); $store.set("checkError", e.message); } }, [downloadComplete]); } catch (e) { console.log("Error starting download:", e.message); $store.set("startError", e.message); } }); } ` // Replace placeholders with actual paths payload = strings.ReplaceAll(payload, "${TEMP_DIR}", tempDir) payload = strings.ReplaceAll(payload, "${SERVER_URL}", server.URL) opts := DefaultTestPluginOptions() opts.Payload = payload opts.Permissions = extension.PluginPermissions{ Scopes: []extension.PluginPermissionScope{ extension.PluginPermissionSystem, }, Allow: extension.PluginAllowlist{ ReadPaths: []string{tempDir + "/**/*"}, WritePaths: []string{tempDir + "/**/*"}, }, } p, logger, manager, _, _, err := InitTestPlugin(t, opts) require.NoError(t, err) // Wait for the plugin to execute and download to complete time.Sleep(12 * time.Second) // Check the store values downloadID, ok := p.store.GetOk("downloadID") require.True(t, ok, "downloadID should be set in store") assert.NotEmpty(t, downloadID) // Check if download completed or if there was an error downloadComplete, ok := p.store.GetOk("downloadComplete") if ok && downloadComplete.(bool) { // If download completed, check file size downloadedSize, ok := p.store.GetOk("downloadedSize") require.True(t, ok, "downloadedSize should be set in store") assert.Equal(t, int64(totalSize), downloadedSize) // Check progress updates progressUpdates, ok := p.store.GetOk("progressUpdates") require.True(t, ok, "progressUpdates should be set in store") updates, ok := progressUpdates.([]interface{}) require.True(t, ok, "progressUpdates should be a slice") // Should have multiple progress updates assert.Greater(t, len(updates), 1, "Should have multiple progress updates") // Print progress updates for debugging logger.Info().Msgf("Received %d progress updates", len(updates)) for i, update := range updates { if i < 5 || i >= len(updates)-5 { logger.Info().Interface("update", update).Msgf("Progress update %d", i) } else if i == 5 { logger.Info().Msg("... more updates ...") } } finalProgress, ok := p.store.GetOk("finalProgress") if ok { progressMap, ok := finalProgress.(*plugin.DownloadProgress) require.Truef(t, ok, "finalProgress should be a map, got %T", finalProgress) assert.Equal(t, "completed", progressMap.Status) assert.InDelta(t, 100.0, progressMap.Percentage, 0.1) } } else { // If download failed, check error downloadError, _ := p.store.GetOk("downloadError") t.Logf("Download error: %v", downloadError) // Don't fail the test if there was an error, just log it } manager.PrintPluginPoolMetrics(opts.ID) } // TestGojaPluginSystemFilepathWalk tests the walk and walkDir functions in the filepath module func TestGojaPluginSystemFilepathWalk(t *testing.T) { // Create a temporary directory for testing tempDir := t.TempDir() // Create a directory structure for testing walk and walkDir dirs := []string{ filepath.Join(tempDir, "dir1"), filepath.Join(tempDir, "dir1", "subdir1"), filepath.Join(tempDir, "dir1", "subdir2"), filepath.Join(tempDir, "dir2"), filepath.Join(tempDir, "dir2", "subdir1"), } files := []string{ filepath.Join(tempDir, "file1.txt"), filepath.Join(tempDir, "file2.txt"), filepath.Join(tempDir, "dir1", "file3.txt"), filepath.Join(tempDir, "dir1", "subdir1", "file4.txt"), filepath.Join(tempDir, "dir1", "subdir2", "file5.txt"), filepath.Join(tempDir, "dir2", "file6.txt"), filepath.Join(tempDir, "dir2", "subdir1", "file7.txt"), } // Create directories for _, dir := range dirs { err := os.MkdirAll(dir, 0755) require.NoError(t, err) } // Create files with content for i, file := range files { content := fmt.Sprintf("Content of file %d", i+1) err := os.WriteFile(file, []byte(content), 0644) require.NoError(t, err) } payload := ` function init() { $ui.register((ctx) => { console.log("Testing $filepath walk and walkDir"); // Test walk const walkPaths = []; const walkErrors = []; $filepath.walk("${TEMP_DIR}", (path, info, err) => { if (err) { console.log("Walk error:", path, err); walkErrors.push({ path, error: err.message }); return; // Continue walking } console.log("Walk path:", path, "isDir:", info.isDir()); walkPaths.push({ path: path, isDir: info.isDir(), name: info.name() }); return; // Continue walking }); console.log("Walk found", walkPaths.length, "paths"); $store.set("walkPaths", walkPaths); $store.set("walkErrors", walkErrors); // Test walkDir const walkDirPaths = []; const walkDirErrors = []; $filepath.walkDir("${TEMP_DIR}", (path, d, err) => { if (err) { console.log("WalkDir error:", path, err); walkDirErrors.push({ path, error: err.message }); return; // Continue walking } console.log("WalkDir path:", path, "isDir:", d.isDir()); walkDirPaths.push({ path: path, isDir: d.isDir(), name: d.name() }); return; // Continue walking }); console.log("WalkDir found", walkDirPaths.length, "paths"); $store.set("walkDirPaths", walkDirPaths); $store.set("walkDirErrors", walkDirErrors); // Count files and directories found const walkFileCount = walkPaths.filter(p => !p.isDir).length; const walkDirCount = walkPaths.filter(p => p.isDir).length; const walkDirFileCount = walkDirPaths.filter(p => !p.isDir).length; const walkDirDirCount = walkDirPaths.filter(p => p.isDir).length; console.log("Walk found", walkFileCount, "files and", walkDirCount, "directories"); console.log("WalkDir found", walkDirFileCount, "files and", walkDirDirCount, "directories"); $store.set("walkFileCount", walkFileCount); $store.set("walkDirCount", walkDirCount); $store.set("walkDirFileCount", walkDirFileCount); $store.set("walkDirDirCount", walkDirDirCount); // Test skipping a directory const skipDirWalkPaths = []; $filepath.walk("${TEMP_DIR}", (path, info, err) => { if (err) { return; // Continue walking } // Skip dir1 and its subdirectories if (info.isDir() && info.name() === "dir1") { console.log("Skipping directory:", path); return $filepath.skipDir; // Skip this directory } skipDirWalkPaths.push(path); return; // Continue walking }); console.log("Skip dir walk found", skipDirWalkPaths.length, "paths"); $store.set("skipDirWalkPaths", skipDirWalkPaths); }); } ` // Replace placeholders with actual paths payload = strings.ReplaceAll(payload, "${TEMP_DIR}", tempDir) opts := DefaultTestPluginOptions() opts.Payload = payload opts.Permissions = extension.PluginPermissions{ Scopes: []extension.PluginPermissionScope{ extension.PluginPermissionSystem, }, Allow: extension.PluginAllowlist{ ReadPaths: []string{tempDir + "/**/*"}, WritePaths: []string{tempDir + "/**/*"}, }, } plugin, _, manager, _, _, err := InitTestPlugin(t, opts) require.NoError(t, err) // Wait for the plugin to execute time.Sleep(1 * time.Second) // Check the store values walkPaths, ok := plugin.store.GetOk("walkPaths") require.True(t, ok, "walkPaths should be set in store") walkPathsSlice, ok := walkPaths.([]interface{}) require.True(t, ok, "walkPaths should be a slice") // Total number of paths should be dirs + files + root dir assert.Equal(t, len(dirs)+len(files)+1, len(walkPathsSlice), "walkPaths should contain all directories and files") walkDirPaths, ok := plugin.store.GetOk("walkDirPaths") require.True(t, ok, "walkDirPaths should be set in store") walkDirPathsSlice, ok := walkDirPaths.([]interface{}) require.True(t, ok, "walkDirPaths should be a slice") // Total number of paths should be dirs + files + root dir assert.Equal(t, len(dirs)+len(files)+1, len(walkDirPathsSlice), "walkDirPaths should contain all directories and files") // Check file and directory counts walkFileCount, ok := plugin.store.GetOk("walkFileCount") require.True(t, ok, "walkFileCount should be set in store") assert.Equal(t, int64(len(files)), walkFileCount, "walkFileCount should match the number of files") walkDirCount, ok := plugin.store.GetOk("walkDirCount") require.True(t, ok, "walkDirCount should be set in store") assert.Equal(t, int64(len(dirs)+1), walkDirCount, "walkDirCount should match the number of directories plus root") walkDirFileCount, ok := plugin.store.GetOk("walkDirFileCount") require.True(t, ok, "walkDirFileCount should be set in store") assert.Equal(t, int64(len(files)), walkDirFileCount, "walkDirFileCount should match the number of files") walkDirDirCount, ok := plugin.store.GetOk("walkDirDirCount") require.True(t, ok, "walkDirDirCount should be set in store") assert.Equal(t, int64(len(dirs)+1), walkDirDirCount, "walkDirDirCount should match the number of directories plus root") // Check skipping directories skipDirWalkPaths, ok := plugin.store.GetOk("skipDirWalkPaths") require.True(t, ok, "skipDirWalkPaths should be set in store") skipDirWalkPathsSlice, ok := skipDirWalkPaths.([]interface{}) require.True(t, ok, "skipDirWalkPaths should be a slice") // Count how many paths should be left after skipping dir1 and its subdirectories // We should have tempDir, dir2, dir2/subdir1, and their files (file1.txt, file2.txt, file6.txt, file7.txt) expectedPathsAfterSkip := 1 + 2 + 4 // root + dir2 dirs + files in root and dir2 assert.Equal(t, expectedPathsAfterSkip, len(skipDirWalkPathsSlice), "skipDirWalkPaths should not contain dir1 and its subdirectories") // Check for errors walkErrors, ok := plugin.store.GetOk("walkErrors") require.True(t, ok, "walkErrors should be set in store") walkErrorsSlice, ok := walkErrors.([]interface{}) require.True(t, ok, "walkErrors should be a slice") assert.Empty(t, walkErrorsSlice, "There should be no walk errors") walkDirErrors, ok := plugin.store.GetOk("walkDirErrors") require.True(t, ok, "walkDirErrors should be set in store") walkDirErrorsSlice, ok := walkDirErrors.([]interface{}) require.True(t, ok, "walkDirErrors should be a slice") assert.Empty(t, walkDirErrorsSlice, "There should be no walkDir errors") manager.PrintPluginPoolMetrics(opts.ID) } // TestGojaPluginSystemCommands tests the command execution functionality in the system module func TestGojaPluginSystemCommands(t *testing.T) { // Create a temporary directory for testing tempDir := t.TempDir() // Create a test file to use with commands testFilePath := filepath.Join(tempDir, "test.txt") err := os.WriteFile(testFilePath, []byte("Hello, world!"), 0644) require.NoError(t, err) payload := ` function init() { $ui.register((ctx) => { console.log("Testing command execution"); // Test executing a simple command try { // Create a command to list files const cmd = $os.cmd("ls", "-la", "${TEMP_DIR}"); // Set up stdout capture const stdoutPipe = cmd.stdoutPipe(); // Start the command cmd.start(); // Read the output const output = $io.readAll(stdoutPipe); console.log("Command output:", $toString(output)); $store.set("commandOutput", $toString(output)); // Wait for the command to complete cmd.wait(); // Check exit code const exitCode = cmd.processState.exitCode(); console.log("Command exit code:", exitCode); $store.set("commandExitCode", exitCode); } catch (e) { console.log("Command execution error:", e.message); $store.set("commandError", e.message); } // Test executing an async command //try { // // Create a command to list files // const asyncCmd = $osExtra.asyncCmd("ls", "-la", "${TEMP_DIR}"); // // // asyncCmd.run((data, err, exitCode, signal) => { // // console.log(data, err, exitCode, signal) // if (data) { // console.log("Async command data:", $toString(data)); // } // if (err) { // console.log("Async command error:", $toString(err)); // } // if (exitCode) { // console.log("Async command exit code:", exitCode); // } // if (signal) { // console.log("Async command signal:", signal); // } // }); //} catch (e) { // console.log("Command execution error:", e.message); // $store.set("asyncCommandError", e.message); //} // // Try unsafe goroutine // try { // // Create a command to list files // const cmd = $os.cmd("ls", "-la", "${TEMP_DIR}"); // $store.watch("unsafeGoroutineOutput", (output) => { // console.log("Unsafe goroutine output:", output); // }); // // Read the output using scanner // $unsafeGoroutine(function() { // // Set up stdout capture // const stdoutPipe = cmd.stdoutPipe(); // console.log("Starting unsafe goroutine"); // const output = $io.readAll(stdoutPipe); // $store.set("unsafeGoroutineOutput", $toString(output)); // console.log("Unsafe goroutine output set", $toString(output)); // cmd.wait(); // }); // // Start the command // cmd.start(); // // Check exit code // const exitCode = cmd.processState.exitCode(); // console.log("Command exit code:", exitCode); // } catch (e) { // console.log("Command execution error:", e.message); // $store.set("unsafeGoroutineError", e.message); // } // Test executing a command with combined output try { // Create a command to find a string in a file const cmd = $os.cmd("grep", "Hello", "${TEST_FILE_PATH}"); // Run the command and capture output const output = cmd.combinedOutput(); console.log("Grep output:", $toString(output)); $store.set("grepOutput", $toString(output)); // Check if the command found the string const foundString = $toString(output).includes("Hello"); console.log("Found string:", foundString); $store.set("foundString", foundString); } catch (e) { console.log("Grep execution error:", e.message); $store.set("grepError", e.message); } // Test executing a command with input try { // Create a command to sort lines const cmd = $os.cmd("sort"); // Set up stdin and stdout pipes const stdinPipe = cmd.stdinPipe(); const stdoutPipe = cmd.stdoutPipe(); // Start the command cmd.start(); // Write to stdin $io.writeString(stdinPipe, "c\nb\na\n"); stdinPipe.close(); // Read sorted output const sortedOutput = $io.readAll(stdoutPipe); console.log("Sorted output:", $toString(sortedOutput)); $store.set("sortedOutput", $toString(sortedOutput)); // Wait for the command to complete cmd.wait(); } catch (e) { console.log("Sort execution error:", e.message); $store.set("sortError", e.message); } // Test unauthorized command try { // Try to execute an unauthorized command const cmd = $os.cmd("open", "https://google.com"); cmd.run(); $store.set("unauthorizedCommandRan", true); } catch (e) { console.log("Unauthorized command error:", e.message); $store.set("unauthorizedCommandError", e.message); $store.set("unauthorizedCommandRan", false); } }); } ` // Replace placeholders with actual paths payload = strings.ReplaceAll(payload, "${TEMP_DIR}", tempDir) payload = strings.ReplaceAll(payload, "${TEST_FILE_PATH}", testFilePath) opts := DefaultTestPluginOptions() opts.Payload = payload opts.Permissions = extension.PluginPermissions{ Scopes: []extension.PluginPermissionScope{ extension.PluginPermissionSystem, }, Allow: extension.PluginAllowlist{ ReadPaths: []string{tempDir + "/**/*"}, WritePaths: []string{tempDir + "/**/*"}, CommandScopes: []extension.CommandScope{ { Command: "ls", Args: []extension.CommandArg{ {Value: "-la"}, {Validator: "$PATH"}, }, }, { Command: "grep", Args: []extension.CommandArg{ {Value: "Hello"}, {Validator: "$PATH"}, }, }, { Command: "sort", Args: []extension.CommandArg{}, }, }, }, } fmt.Println(opts.Permissions.GetDescription()) plugin, _, manager, _, _, err := InitTestPlugin(t, opts) require.NoError(t, err) // Wait for the plugin to execute time.Sleep(1 * time.Second) // Check the store values for the ls command commandOutput, ok := plugin.store.GetOk("commandOutput") require.True(t, ok, "commandOutput should be set in store") assert.Contains(t, commandOutput.(string), "test.txt", "Command output should contain the test file") commandExitCode, ok := plugin.store.GetOk("commandExitCode") require.True(t, ok, "commandExitCode should be set in store") assert.Equal(t, int64(0), commandExitCode, "Command exit code should be 0") // Check the store values for the grep command grepOutput, ok := plugin.store.GetOk("grepOutput") if ok { assert.Contains(t, grepOutput.(string), "Hello", "Grep output should contain 'Hello'") } foundString, ok := plugin.store.GetOk("foundString") if ok { assert.True(t, foundString.(bool), "Should have found the string in the file") } // Check the store values for the sort command sortedOutput, ok := plugin.store.GetOk("sortedOutput") if ok { // Expected output: "a\nb\nc\n" (sorted) assert.Contains(t, sortedOutput.(string), "a", "Sorted output should contain 'a'") assert.Contains(t, sortedOutput.(string), "b", "Sorted output should contain 'b'") assert.Contains(t, sortedOutput.(string), "c", "Sorted output should contain 'c'") // Check if the lines are in the correct order lines := strings.Split(strings.TrimSpace(sortedOutput.(string)), "\n") if len(lines) >= 3 { assert.Equal(t, "a", lines[0], "First line should be 'a'") assert.Equal(t, "b", lines[1], "Second line should be 'b'") assert.Equal(t, "c", lines[2], "Third line should be 'c'") } } // Check that unauthorized command was rejected unauthorizedCommandRan, ok := plugin.store.GetOk("unauthorizedCommandRan") require.True(t, ok, "unauthorizedCommandRan should be set in store") assert.False(t, unauthorizedCommandRan.(bool), "Unauthorized command should not have run") unauthorizedCommandError, ok := plugin.store.GetOk("unauthorizedCommandError") require.True(t, ok, "unauthorizedCommandError should be set in store") assert.Contains(t, unauthorizedCommandError.(string), "not authorized", "Error should indicate command was not authorized") manager.PrintPluginPoolMetrics(opts.ID) } func TestGojaPluginSystemAsyncCommand(t *testing.T) { // Create a temporary directory for testing tempDir := t.TempDir() // Create a test file to use with commands testFilePath := filepath.Join(tempDir, "test.txt") err := os.WriteFile(testFilePath, []byte("Hello, world!"), 0644) require.NoError(t, err) payload := ` function init() { $ui.register((ctx) => { console.log("Testing async command execution"); // Test executing an async command try { // Create a command to list files let asyncCmd = $osExtra.asyncCmd("ls", "-la", "${TEMP_DIR}"); let output = ""; asyncCmd.run((data, err, exitCode, signal) => { // console.log(data, err, exitCode, signal) if (data) { // console.log("Async command data:", $toString(data)); output += $toString(data) + "\n"; $store.set("asyncCommandData", $toString(output)); } if (err) { console.log("Async command error:", $toString(err)); $store.set("asyncCommandError", $toString(err)); } if (exitCode !== undefined) { console.log("output 1", output) console.log("Async command exit code:", exitCode); $store.set("asyncCommandExitCode", exitCode); console.log("Async command signal:", signal); $store.set("asyncCommandSignal", signal); } }); console.log("Running second command") let asyncCmd2 = $osExtra.asyncCmd("ls", "-la", "${TEMP_DIR}"); let output2 = ""; asyncCmd2.run((data, err, exitCode, signal) => { // console.log(data, err, exitCode, signal) if (data) { // console.log("Async command data:", $toString(data)); output2 += $toString(data) + "\n"; $store.set("asyncCommandData", $toString(output2)); } if (err) { console.log("Async command error:", $toString(err)); $store.set("asyncCommandError", $toString(err)); } if (exitCode !== undefined) { console.log("output 2", output2) console.log("Async command exit code:", exitCode); $store.set("asyncCommandExitCode", exitCode); console.log("Async command signal:", signal); $store.set("asyncCommandSignal", signal); } }); } catch (e) { console.log("Command execution error:", e.message); $store.set("asyncCommandError", e.message); } }); } ` // Replace placeholders with actual paths payload = strings.ReplaceAll(payload, "${TEMP_DIR}", tempDir) payload = strings.ReplaceAll(payload, "${TEST_FILE_PATH}", testFilePath) opts := DefaultTestPluginOptions() opts.Payload = payload opts.Permissions = extension.PluginPermissions{ Scopes: []extension.PluginPermissionScope{ extension.PluginPermissionSystem, }, Allow: extension.PluginAllowlist{ ReadPaths: []string{tempDir + "/**/*"}, WritePaths: []string{tempDir + "/**/*"}, CommandScopes: []extension.CommandScope{ { Command: "ls", Args: []extension.CommandArg{ {Value: "-la"}, {Validator: "$PATH"}, }, }, }, }, } plugin, _, manager, _, _, err := InitTestPlugin(t, opts) require.NoError(t, err) // Wait for the plugin to execute time.Sleep(2 * time.Second) // Check the store values for the ls command asyncCommandData, ok := plugin.store.GetOk("asyncCommandData") require.True(t, ok, "asyncCommandData should be set in store") assert.Contains(t, asyncCommandData.(string), "test.txt", "Command output should contain the test file") asyncCommandExitCode, ok := plugin.store.GetOk("asyncCommandExitCode") require.True(t, ok, "asyncCommandExitCode should be set in store") assert.Equal(t, int64(0), asyncCommandExitCode, "Command exit code should be 0") manager.PrintPluginPoolMetrics(opts.ID) } // TestGojaPluginSystemUnzip tests the unzip functionality in the osExtra module func TestGojaPluginSystemUnzip(t *testing.T) { // Create a temporary directory for testing tempDir := t.TempDir() // Create a test zip file zipPath := filepath.Join(tempDir, "test.zip") extractPath := filepath.Join(tempDir, "extracted") // Create a zip file with test content err := createTestZipFile(zipPath) require.NoError(t, err, "Failed to create test zip file") payload := ` function init() { $ui.register((ctx) => { console.log("Testing $osExtra unzip functionality"); try { // Create extraction directory $os.mkdirAll("${EXTRACT_PATH}", 0755); // Unzip the file $osExtra.unzip("${ZIP_PATH}", "${EXTRACT_PATH}"); console.log("Unzip successful"); $store.set("unzipSuccess", true); // Check if files were extracted const entries = $os.readDir("${EXTRACT_PATH}"); const fileNames = entries.map(entry => entry.name()); console.log("Extracted files:", fileNames); $store.set("extractedFiles", fileNames); // Read content of extracted file const content = $os.readFile("${EXTRACT_PATH}/test.txt"); console.log("Extracted content:", $toString(content)); $store.set("extractedContent", $toString(content)); // Try to unzip to an unauthorized location try { $osExtra.unzip("${ZIP_PATH}", "/tmp/unauthorized"); $store.set("unauthorizedUnzipSuccess", true); } catch (e) { console.log("Unauthorized unzip error:", e.message); $store.set("unauthorizedUnzipError", e.message); $store.set("unauthorizedUnzipSuccess", false); } } catch (e) { console.log("Unzip error:", e.message); $store.set("unzipError", e.message); } }); } ` // Replace placeholders with actual paths payload = strings.ReplaceAll(payload, "${ZIP_PATH}", zipPath) payload = strings.ReplaceAll(payload, "${EXTRACT_PATH}", extractPath) opts := DefaultTestPluginOptions() opts.Payload = payload opts.Permissions = extension.PluginPermissions{ Scopes: []extension.PluginPermissionScope{ extension.PluginPermissionSystem, }, Allow: extension.PluginAllowlist{ ReadPaths: []string{tempDir + "/**/*"}, WritePaths: []string{tempDir + "/**/*"}, }, } plugin, _, manager, _, _, err := InitTestPlugin(t, opts) require.NoError(t, err) // Wait for the plugin to execute time.Sleep(1 * time.Second) // Check the store values unzipSuccess, ok := plugin.store.GetOk("unzipSuccess") require.True(t, ok, "unzipSuccess should be set in store") assert.True(t, unzipSuccess.(bool), "Unzip should have succeeded") extractedFiles, ok := plugin.store.GetOk("extractedFiles") require.True(t, ok, "extractedFiles should be set in store") filesSlice, ok := extractedFiles.([]interface{}) require.True(t, ok, "extractedFiles should be a slice") assert.Contains(t, filesSlice, "test.txt", "Extracted files should include test.txt") extractedContent, ok := plugin.store.GetOk("extractedContent") require.True(t, ok, "extractedContent should be set in store") assert.Equal(t, "Test content for zip file", extractedContent, "Extracted content should match original") // Check unauthorized unzip attempt unauthorizedUnzipSuccess, ok := plugin.store.GetOk("unauthorizedUnzipSuccess") require.True(t, ok, "unauthorizedUnzipSuccess should be set in store") assert.False(t, unauthorizedUnzipSuccess.(bool), "Unauthorized unzip should have failed") unauthorizedUnzipError, ok := plugin.store.GetOk("unauthorizedUnzipError") require.True(t, ok, "unauthorizedUnzipError should be set in store") assert.Contains(t, unauthorizedUnzipError.(string), "not authorized", "Error should indicate path not authorized") manager.PrintPluginPoolMetrics(opts.ID) } // TestGojaPluginSystemMime tests the mime module functionality func TestGojaPluginSystemMime(t *testing.T) { payload := ` function init() { $ui.register((ctx) => { console.log("Testing $mime functionality"); try { // Test parsing content type const contentType = "text/html; charset=utf-8"; const parsed = $mime.parse(contentType); console.log("Parsed content type:", parsed); $store.set("parsedContentType", parsed); // Test parsing content type with multiple parameters const contentTypeWithParams = "application/json; charset=utf-8; boundary=something"; const parsedWithParams = $mime.parse(contentTypeWithParams); console.log("Parsed content type with params:", parsedWithParams); $store.set("parsedContentTypeWithParams", parsedWithParams); // Test formatting content type const formatted = $mime.format("text/plain", { charset: "utf-8", boundary: "boundary" }); console.log("Formatted content type:", formatted); $store.set("formattedContentType", formatted); // Test parsing invalid content type try { const invalidContentType = "invalid content type"; const parsedInvalid = $mime.parse(invalidContentType); console.log("Parsed invalid content type:", parsedInvalid); $store.set("parsedInvalidContentType", parsedInvalid); } catch (e) { console.log("Invalid content type error:", e.message); $store.set("invalidContentTypeError", e.message); } } catch (e) { console.log("Mime test error:", e.message); $store.set("mimeTestError", e.message); } }); } ` opts := DefaultTestPluginOptions() opts.Payload = payload opts.Permissions = extension.PluginPermissions{ Scopes: []extension.PluginPermissionScope{ extension.PluginPermissionSystem, }, } plugin, _, manager, _, _, err := InitTestPlugin(t, opts) require.NoError(t, err) // Wait for the plugin to execute time.Sleep(1 * time.Second) // Check the store values for parsed content type parsedContentType, ok := plugin.store.GetOk("parsedContentType") require.True(t, ok, "parsedContentType should be set in store") parsedMap, ok := parsedContentType.(map[string]interface{}) require.True(t, ok, "parsedContentType should be a map") // Check media type mediaType, ok := parsedMap["mediaType"] require.True(t, ok, "mediaType should be in parsed result") assert.Equal(t, "text/html", mediaType, "Media type should be text/html") // Check parameters parameters, ok := parsedMap["parameters"] require.True(t, ok, "parameters should be in parsed result") paramsMap, ok := parameters.(map[string]string) require.Truef(t, ok, "parameters should be a map but got %T", parameters) assert.Equal(t, "utf-8", paramsMap["charset"], "charset parameter should be utf-8") // Check parsed content type with multiple parameters parsedWithParams, ok := plugin.store.GetOk("parsedContentTypeWithParams") require.True(t, ok, "parsedContentTypeWithParams should be set in store") parsedWithParamsMap, ok := parsedWithParams.(map[string]interface{}) require.Truef(t, ok, "parsedContentTypeWithParams should be a map but got %T", parsedWithParams) // Check media type mediaTypeWithParams, ok := parsedWithParamsMap["mediaType"] require.True(t, ok, "mediaType should be in parsed result") assert.Equal(t, "application/json", mediaTypeWithParams, "Media type should be application/json") // Check parameters parametersWithParams, ok := parsedWithParamsMap["parameters"] require.True(t, ok, "parameters should be in parsed result") require.Truef(t, ok, "parameters should be a map but got %T", parametersWithParams) // Check formatted content type formattedContentType, ok := plugin.store.GetOk("formattedContentType") require.True(t, ok, "formattedContentType should be set in store") assert.Contains(t, formattedContentType.(string), "text/plain", "Formatted content type should contain text/plain") assert.Contains(t, formattedContentType.(string), "charset=utf-8", "Formatted content type should contain charset=utf-8") assert.Contains(t, formattedContentType.(string), "boundary=boundary", "Formatted content type should contain boundary=boundary") // Check invalid content type error invalidContentTypeError, ok := plugin.store.GetOk("invalidContentTypeError") if ok { assert.NotEmpty(t, invalidContentTypeError, "Invalid content type should have produced an error") } manager.PrintPluginPoolMetrics(opts.ID) } // Helper function to create a test zip file func createTestZipFile(zipPath string) error { // Create a buffer to write our zip to zipFile, err := os.Create(zipPath) if err != nil { return err } defer zipFile.Close() // Create a new zip archive zipWriter := zip.NewWriter(zipFile) defer zipWriter.Close() // Add a file to the archive fileWriter, err := zipWriter.Create("test.txt") if err != nil { return err } // Write content to the file _, err = fileWriter.Write([]byte("Test content for zip file")) if err != nil { return err } // Add a directory to the archive _, err = zipWriter.Create("testdir/") if err != nil { return err } // Add a file in the directory dirFileWriter, err := zipWriter.Create("testdir/nested.txt") if err != nil { return err } // Write content to the nested file _, err = dirFileWriter.Write([]byte("Nested file content")) if err != nil { return err } return nil }