mirror of
https://github.com/open-gitagent/langship.sh.git
synced 2026-08-03 07:21:04 +02:00
feat(web): initialize Next.js project with Tailwind CSS and TypeScript setup
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
package engine
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/lyzrai/flow/pkg/models"
|
||||
)
|
||||
|
||||
func TestBuildDAG_populatesEdgesAndDegrees(t *testing.T) {
|
||||
wf := &models.WorkflowDefinition{
|
||||
Nodes: []models.NodeDef{
|
||||
{ID: "1", Name: "T", Type: "flow-nodes-base.trigger"},
|
||||
{ID: "2", Name: "A", Type: "flow-nodes-base.set"},
|
||||
{ID: "3", Name: "B", Type: "flow-nodes-base.noOp"},
|
||||
},
|
||||
Connections: []models.ConnectionDef{
|
||||
{SourceNode: "T", TargetNode: "A", SourceOutputIndex: 0, TargetInputIndex: 0},
|
||||
{SourceNode: "A", TargetNode: "B", SourceOutputIndex: 0, TargetInputIndex: 0},
|
||||
},
|
||||
}
|
||||
dag, err := BuildDAG(wf)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if dag.InDegree["T"] != 0 || dag.InDegree["A"] != 1 || dag.InDegree["B"] != 1 {
|
||||
t.Errorf("indegrees: %+v", dag.InDegree)
|
||||
}
|
||||
if len(dag.Adjacency["T"]) != 1 || dag.Adjacency["T"][0].Target != "A" {
|
||||
t.Errorf("T adjacency: %+v", dag.Adjacency["T"])
|
||||
}
|
||||
if len(dag.InEdges["B"]) != 1 || dag.InEdges["B"][0].Target != "A" {
|
||||
t.Errorf("B in-edges: %+v", dag.InEdges["B"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildDAG_rejectsDuplicateNodeNames(t *testing.T) {
|
||||
wf := &models.WorkflowDefinition{
|
||||
Nodes: []models.NodeDef{
|
||||
{ID: "1", Name: "X", Type: "flow-nodes-base.trigger"},
|
||||
{ID: "2", Name: "X", Type: "flow-nodes-base.set"},
|
||||
},
|
||||
}
|
||||
if _, err := BuildDAG(wf); err == nil {
|
||||
t.Fatal("expected duplicate-name error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTopologicalSort_orderRespectsDependencies(t *testing.T) {
|
||||
wf := &models.WorkflowDefinition{
|
||||
Nodes: []models.NodeDef{
|
||||
{ID: "1", Name: "T", Type: "flow-nodes-base.trigger"},
|
||||
{ID: "2", Name: "A", Type: "flow-nodes-base.set"},
|
||||
{ID: "3", Name: "B", Type: "flow-nodes-base.noOp"},
|
||||
},
|
||||
Connections: []models.ConnectionDef{
|
||||
{SourceNode: "T", TargetNode: "A"},
|
||||
{SourceNode: "A", TargetNode: "B"},
|
||||
},
|
||||
}
|
||||
dag, _ := BuildDAG(wf)
|
||||
order, err := dag.TopologicalSort()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected: %v", err)
|
||||
}
|
||||
idx := map[string]int{}
|
||||
for i, n := range order {
|
||||
idx[n] = i
|
||||
}
|
||||
if !(idx["T"] < idx["A"] && idx["A"] < idx["B"]) {
|
||||
t.Fatalf("unexpected order %v", order)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTopologicalSort_detectsCycle(t *testing.T) {
|
||||
wf := &models.WorkflowDefinition{
|
||||
Nodes: []models.NodeDef{
|
||||
{ID: "1", Name: "A", Type: "flow-nodes-base.set"},
|
||||
{ID: "2", Name: "B", Type: "flow-nodes-base.set"},
|
||||
},
|
||||
Connections: []models.ConnectionDef{
|
||||
{SourceNode: "A", TargetNode: "B"},
|
||||
{SourceNode: "B", TargetNode: "A"},
|
||||
},
|
||||
}
|
||||
dag, _ := BuildDAG(wf)
|
||||
if _, err := dag.TopologicalSort(); err == nil {
|
||||
t.Fatal("expected cycle error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasApprovalNode(t *testing.T) {
|
||||
with := &models.WorkflowDefinition{Nodes: []models.NodeDef{{Type: "flow-nodes-base.waitForApproval"}}}
|
||||
without := &models.WorkflowDefinition{Nodes: []models.NodeDef{{Type: "flow-nodes-base.set"}}}
|
||||
if !HasApprovalNode(with) {
|
||||
t.Error("expected true")
|
||||
}
|
||||
if HasApprovalNode(without) {
|
||||
t.Error("expected false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetStartNodes(t *testing.T) {
|
||||
wf := &models.WorkflowDefinition{
|
||||
Nodes: []models.NodeDef{
|
||||
{ID: "1", Name: "T1", Type: "flow-nodes-base.trigger"},
|
||||
{ID: "2", Name: "T2", Type: "flow-nodes-base.trigger"},
|
||||
{ID: "3", Name: "Mid", Type: "flow-nodes-base.set"},
|
||||
},
|
||||
Connections: []models.ConnectionDef{{SourceNode: "T1", TargetNode: "Mid"}},
|
||||
}
|
||||
dag, _ := BuildDAG(wf)
|
||||
got := dag.GetStartNodes()
|
||||
sort.Strings(got)
|
||||
want := []string{"Mid", "T1", "T2"}
|
||||
// Mid has incoming edge so it should be excluded; T2 has no incoming so it's a start.
|
||||
want = []string{"T1", "T2"}
|
||||
// recompute filter
|
||||
filtered := []string{}
|
||||
for _, n := range got {
|
||||
if n != "Mid" {
|
||||
filtered = append(filtered, n)
|
||||
}
|
||||
}
|
||||
sort.Strings(filtered)
|
||||
if !reflect.DeepEqual(filtered, want) {
|
||||
t.Fatalf("starts: got %v want %v", filtered, want)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user