bf9395e022
CI / license-header (push) Has been skipped
CI / e2e-dry-run (push) Has been skipped
CI / fast-gate (push) Failing after 0s
Test PR Label Logic / test-pr-labels (push) Failing after 1s
Skill Format Check / check-format (push) Failing after 2s
CI / security (push) Failing after 5s
CI / unit-test (push) Has been skipped
CI / lint (push) Has been skipped
CI / script-test (push) Has been skipped
CI / deterministic-gate (push) Has been skipped
CI / coverage (push) Has been skipped
CI / results (push) Has been cancelled
CI / deadcode (push) Has been cancelled
CI / e2e-live (push) Has been cancelled
120 lines
4.1 KiB
Go
120 lines
4.1 KiB
Go
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package internalplatform_test
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/larksuite/cli/extension/platform"
|
|
"github.com/larksuite/cli/internal/hook"
|
|
internalplatform "github.com/larksuite/cli/internal/platform"
|
|
)
|
|
|
|
func TestBuildInventory_groupsByPluginName(t *testing.T) {
|
|
plugins := []internalplatform.PluginInventorySource{
|
|
{Name: "a", Version: "1.0", Capabilities: platform.Capabilities{
|
|
Restricts: true, FailurePolicy: platform.FailClosed,
|
|
}},
|
|
{Name: "b", Version: "2.0"},
|
|
}
|
|
|
|
r := hook.NewRegistry()
|
|
obs := func(context.Context, platform.Invocation) {}
|
|
wrap := func(next platform.Handler) platform.Handler { return next }
|
|
lc := func(context.Context, *platform.LifecycleContext) error { return nil }
|
|
|
|
r.AddObserver(hook.ObserverEntry{Name: "a.pre", When: platform.Before, Selector: platform.All(), Fn: obs})
|
|
r.AddObserver(hook.ObserverEntry{Name: "a.post", When: platform.After, Selector: platform.All(), Fn: obs})
|
|
r.AddObserver(hook.ObserverEntry{Name: "b.audit", When: platform.Before, Selector: platform.All(), Fn: obs})
|
|
r.AddWrapper(hook.WrapperEntry{Name: "a.approval", Selector: platform.All(), Fn: wrap})
|
|
r.AddLifecycle(hook.LifecycleEntry{Name: "a.boot", Event: platform.Startup, Fn: lc})
|
|
r.AddLifecycle(hook.LifecycleEntry{Name: "b.bye", Event: platform.Shutdown, Fn: lc})
|
|
|
|
rules := []internalplatform.RuleInventorySource{
|
|
{PluginName: "a", RuleName: "a-rule", Allow: []string{"docs/**"}, MaxRisk: "read"},
|
|
}
|
|
|
|
inv := internalplatform.BuildInventory(plugins, r, rules)
|
|
|
|
if got := len(inv.Plugins); got != 2 {
|
|
t.Fatalf("Plugins len = %d, want 2", got)
|
|
}
|
|
a := findPlugin(inv, "a")
|
|
b := findPlugin(inv, "b")
|
|
if a == nil || b == nil {
|
|
t.Fatalf("missing entries: a=%v b=%v", a, b)
|
|
}
|
|
|
|
if got := len(a.Observers); got != 2 {
|
|
t.Errorf("a.Observers = %d, want 2", got)
|
|
}
|
|
if got := len(a.Wrappers); got != 1 {
|
|
t.Errorf("a.Wrappers = %d, want 1", got)
|
|
}
|
|
if got := len(a.Lifecycles); got != 1 {
|
|
t.Errorf("a.Lifecycles = %d, want 1", got)
|
|
}
|
|
if len(a.Rules) != 1 || a.Rules[0].Name != "a-rule" {
|
|
t.Errorf("a.Rules = %+v, want single rule name a-rule", a.Rules)
|
|
}
|
|
if a.Capabilities.FailurePolicy != "FailClosed" {
|
|
t.Errorf("a.Capabilities.FailurePolicy = %q, want FailClosed", a.Capabilities.FailurePolicy)
|
|
}
|
|
|
|
if got := len(b.Observers); got != 1 {
|
|
t.Errorf("b.Observers = %d, want 1 (only b.audit)", got)
|
|
}
|
|
if len(b.Rules) != 0 {
|
|
t.Errorf("b.Rules = %+v, want empty (b did not call Restrict)", b.Rules)
|
|
}
|
|
if b.Capabilities.FailurePolicy != "FailOpen" {
|
|
t.Errorf("b.Capabilities.FailurePolicy = %q, want FailOpen (zero value)", b.Capabilities.FailurePolicy)
|
|
}
|
|
}
|
|
|
|
// A plugin contributing several rules (same PluginName, multiple
|
|
// RuleInventorySource entries) must surface ALL of them under Rules, in
|
|
// order -- not silently overwrite down to the last one. Pins the
|
|
// multi-rule inventory fix.
|
|
func TestBuildInventory_multipleRulesPerPlugin(t *testing.T) {
|
|
plugins := []internalplatform.PluginInventorySource{
|
|
{Name: "a", Version: "1.0", Capabilities: platform.Capabilities{
|
|
Restricts: true, FailurePolicy: platform.FailClosed,
|
|
}},
|
|
}
|
|
rules := []internalplatform.RuleInventorySource{
|
|
{PluginName: "a", RuleName: "docs-ro", Allow: []string{"docs/**"}, MaxRisk: "read"},
|
|
{PluginName: "a", RuleName: "im-rw", Allow: []string{"im/**"}, MaxRisk: "write"},
|
|
}
|
|
|
|
inv := internalplatform.BuildInventory(plugins, nil, rules)
|
|
a := findPlugin(inv, "a")
|
|
if a == nil {
|
|
t.Fatalf("missing entry a")
|
|
}
|
|
if len(a.Rules) != 2 {
|
|
t.Fatalf("a.Rules = %d, want 2 (both rules preserved, no overwrite)", len(a.Rules))
|
|
}
|
|
if a.Rules[0].Name != "docs-ro" || a.Rules[1].Name != "im-rw" {
|
|
t.Errorf("rules out of order: %q, %q", a.Rules[0].Name, a.Rules[1].Name)
|
|
}
|
|
}
|
|
|
|
func TestBuildInventory_empty(t *testing.T) {
|
|
inv := internalplatform.BuildInventory(nil, nil, nil)
|
|
if got := len(inv.Plugins); got != 0 {
|
|
t.Errorf("Plugins len = %d, want 0", got)
|
|
}
|
|
}
|
|
|
|
func findPlugin(inv *internalplatform.Inventory, name string) *internalplatform.PluginEntry {
|
|
for i := range inv.Plugins {
|
|
if inv.Plugins[i].Name == name {
|
|
return &inv.Plugins[i]
|
|
}
|
|
}
|
|
return nil
|
|
}
|