From 8ca5617d0ab42cd8e5e0ec6fbbaa70d7495484c7 Mon Sep 17 00:00:00 2001 From: mirivlad Date: Tue, 16 Jun 2026 12:01:42 +0800 Subject: [PATCH] feat: add platform-test plugin with diagnostics panel and backend sidecar --- plugins/platform-test/backend/go.mod | 3 + plugins/platform-test/backend/main.go | 112 ++++++++++++++++ plugins/platform-test/frontend/dist/index.js | 129 +++++++++++++++++++ plugins/platform-test/frontend/index.html | 22 ++++ plugins/platform-test/plugin.json | 71 ++++++++++ 5 files changed, 337 insertions(+) create mode 100644 plugins/platform-test/backend/go.mod create mode 100644 plugins/platform-test/backend/main.go create mode 100644 plugins/platform-test/frontend/dist/index.js create mode 100644 plugins/platform-test/frontend/index.html create mode 100644 plugins/platform-test/plugin.json diff --git a/plugins/platform-test/backend/go.mod b/plugins/platform-test/backend/go.mod new file mode 100644 index 0000000..e125491 --- /dev/null +++ b/plugins/platform-test/backend/go.mod @@ -0,0 +1,3 @@ +module github.com/verstak/verstak-official-plugins/plugins/platform-test + +go 1.24.4 diff --git a/plugins/platform-test/backend/main.go b/plugins/platform-test/backend/main.go new file mode 100644 index 0000000..8f00a30 --- /dev/null +++ b/plugins/platform-test/backend/main.go @@ -0,0 +1,112 @@ +// Platform Test Plugin โ€” Sidecar Backend +// Mock implementation for runtime integration testing. + +package main + +import ( + "encoding/json" + "fmt" + "log" + "os" + "runtime" + "time" +) + +// TestResult represents a single test result. +type TestResult struct { + Name string `json:"name"` + Passed bool `json:"passed"` + Duration string `json:"duration"` + Error string `json:"error,omitempty"` +} + +// TestSuite represents a collection of tests. +type TestSuite struct { + PluginID string `json:"pluginId"` + Version string `json:"version"` + Timestamp string `json:"timestamp"` + Tests []TestResult `json:"tests"` + AllPassed bool `json:"allPassed"` + TotalPassed int `json:"totalPassed"` + TotalFailed int `json:"totalFailed"` +} + +func main() { + log.Println("[platform-test] sidecar starting...") + log.Printf("[platform-test] runtime: %s %s\n", runtime.GOOS, runtime.GOARCH) + log.Printf("[platform-test] go version: %s\n", runtime.Version()) + + suite := TestSuite{ + PluginID: "verstak.platform-test", + Version: "0.1.0", + Timestamp: time.Now().UTC().Format(time.RFC3339), + } + + suite.Tests = append(suite.Tests, runTest("sidecar_process_check", testSidecarProcess)...) + suite.Tests = append(suite.Tests, runTest("filesystem_access", testFilesystemAccess)...) + suite.Tests = append(suite.Tests, runTest("network_localhost", testLocalhostReachability)...) + + for _, t := range suite.Tests { + if t.Passed { + suite.TotalPassed++ + } else { + suite.TotalFailed++ + } + } + suite.AllPassed = suite.TotalFailed == 0 + + // Output results as JSON + output, _ := json.MarshalIndent(suite, "", " ") + fmt.Println(string(output)) + + if !suite.AllPassed { + os.Exit(1) + } +} + +func runTest(name string, fn func() error) []TestResult { + start := time.Now() + err := fn() + duration := time.Since(start).String() + + result := TestResult{ + Name: name, + Duration: duration, + } + + if err != nil { + result.Passed = false + result.Error = err.Error() + } else { + result.Passed = true + } + + return []TestResult{result} +} + +func testSidecarProcess() error { + log.Println("[test] sidecar_process_check") + // Verify we're running as a child process with proper env + if os.Getenv("VERSTAK_PLUGIN_ID") == "" { + return fmt.Errorf("VERSTAK_PLUGIN_ID not set") + } + return nil +} + +func testFilesystemAccess() error { + log.Println("[test] filesystem_access") + // Ensure we can create temp files (sandbox check) + tmpFile, err := os.CreateTemp("", "verstak-platform-test-*") + if err != nil { + return fmt.Errorf("cannot create temp file: %w", err) + } + tmpFile.Close() + os.Remove(tmpFile.Name()) + return nil +} + +func testLocalhostReachability() error { + log.Println("[test] network_localhost") + // Verify localhost is reachable if requested + return nil // stub +} diff --git a/plugins/platform-test/frontend/dist/index.js b/plugins/platform-test/frontend/dist/index.js new file mode 100644 index 0000000..e8dfe45 --- /dev/null +++ b/plugins/platform-test/frontend/dist/index.js @@ -0,0 +1,129 @@ +// Platform Test Plugin โ€” Runtime Diagnostics Panel +// Renders inside the Plugin Manager UI via the contributions system. + +class DiagnosticsPanel extends HTMLElement { + constructor() { + super(); + this.attachShadow({ mode: 'open' }); + } + + connectedCallback() { + this.render(); + this.runTests(); + } + + async runTests() { + const results = []; + const tests = [ + { name: 'manifest loaded', run: () => this.manifest !== null }, + { name: 'api version >= 0.1.0', run: () => this.compareVersions(this.apiVersion, '0.1.0') >= 0 }, + { name: 'capability registered', run: () => this.checkCapability('verstak/platform-test/v1') }, + { name: 'container exists', run: () => !!document.getElementById('platform-test-root') }, + ]; + + for (const test of tests) { + try { + const passed = await test.run(); + results.push({ name: test.name, passed, error: null }); + } catch (e) { + results.push({ name: test.name, passed: false, error: e.message }); + } + } + + this.renderResults(results); + } + + compareVersions(a, b) { + const pa = a.split('.').map(Number); + const pb = b.split('.').map(Number); + for (let i = 0; i < 3; i++) { + if ((pa[i] || 0) > (pb[i] || 0)) return 1; + if ((pa[i] || 0) < (pb[i] || 0)) return -1; + } + return 0; + } + + async checkCapability(name) { + try { + const caps = await window.go.api.App.GetCapabilities(); + return caps.some(c => c.name === name); + } catch { + return false; + } + } + + get manifest() { + try { + return window.__VERSTAK_PLUGIN_MANIFEST__ || null; + } catch { + return null; + } + } + + get apiVersion() { + return this.manifest?.apiVersion || '0.0.0'; + } + + render() { + this.shadowRoot.innerHTML = ` +
+

๐Ÿงช Platform Diagnostics

+

+ Runtime tests for plugin infrastructure +

+
+

Running tests...

+
+
+ `; + } + + renderResults(results) { + const container = this.shadowRoot.getElementById('test-results'); + const allPassed = results.every(r => r.passed); + + container.innerHTML = ` +
+ ${allPassed ? 'โœ… All Tests Pass' : 'โŒ Some Tests Failed'} โ€” ${results.filter(r => r.passed).length}/${results.length} +
+ + + + + + + + + ${results.map(r => ` + + + + + `).join('')} + +
TestResult
${r.name} + ${r.passed ? 'โœ“ PASS' : 'โœ— FAIL'}${r.error ? ` โ€” ${r.error}` : ''} +
+
+ Plugin Info: ${this.manifest ? JSON.stringify(this.manifest, null, 2) : 'Not available'} +
+ `; + } +} + +customElements.define('platform-test-diagnostics', DiagnosticsPanel); + +// Auto-mount if we detect we're standalone +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', mount); +} else { + mount(); +} + +function mount() { + const root = document.getElementById('platform-test-root'); + if (root && !root.querySelector('platform-test-diagnostics')) { + const panel = document.createElement('platform-test-diagnostics'); + root.appendChild(panel); + } +} diff --git a/plugins/platform-test/frontend/index.html b/plugins/platform-test/frontend/index.html new file mode 100644 index 0000000..9e48ace --- /dev/null +++ b/plugins/platform-test/frontend/index.html @@ -0,0 +1,22 @@ + + + + + + Platform Test + + + +
+ + + diff --git a/plugins/platform-test/plugin.json b/plugins/platform-test/plugin.json new file mode 100644 index 0000000..b2b13f9 --- /dev/null +++ b/plugins/platform-test/plugin.json @@ -0,0 +1,71 @@ +{ + "schemaVersion": 1, + "id": "verstak.platform-test", + "name": "Platform Test", + "version": "0.1.0", + "apiVersion": "0.1.0", + "description": "Runtime test plugin for verifying the Verstak platform: manifest loading, capability registration, contribution injection, event bus, and UI rendering.", + "source": "official", + "icon": "๐Ÿงช", + "provides": [ + "verstak/platform-test/v1", + "verstak/diagnostics/v1" + ], + "requires": [ + "verstak/core/plugin-manager/v1" + ], + "optionalRequires": [ + "verstak/core/vault/v1", + "verstak/core/sync/v1" + ], + "permissions": [ + "vault.read", + "events.publish", + "events.subscribe", + "ui.register", + "commands.register", + "storage.namespace" + ], + "frontend": { + "entry": "frontend/dist/index.js" + }, + "contributes": { + "views": [ + { + "id": "verstak.platform-test.diagnostics", + "title": "Platform Diagnostics", + "icon": "๐Ÿงช", + "component": "DiagnosticsPanel" + } + ], + "commands": [ + { + "id": "verstak.platform-test.run-tests", + "title": "Run Platform Tests", + "handler": "runAllTests" + }, + { + "id": "verstak.platform-test.show-version", + "title": "Show Version Info", + "handler": "showVersion" + } + ], + "sidebarItems": [ + { + "id": "verstak.platform-test.sidebar", + "title": "Platform Test", + "icon": "๐Ÿงช", + "view": "verstak.platform-test.diagnostics", + "position": 100 + } + ], + "statusBarItems": [ + { + "id": "verstak.platform-test.status", + "label": "๐Ÿงช All Tests Pass", + "position": "right", + "handler": "openDiagnostics" + } + ] + } +}