Files
dingtalk-workspace-cli/test/scripts/reviewer_router_workflow_test.go
T

164 lines
5.1 KiB
Go

// Copyright 2026 Alibaba Group
// Licensed under the Apache License, Version 2.0 (the "License");
package scripts_test
import (
"os"
"path/filepath"
"reflect"
"strings"
"testing"
"gopkg.in/yaml.v3"
)
type reviewerRouterWorkflow struct {
On map[string]reviewerRouterTrigger `yaml:"on"`
Permissions map[string]string `yaml:"permissions"`
Jobs map[string]reviewerRouterJob `yaml:"jobs"`
}
type reviewerRouterTrigger struct {
Branches []string `yaml:"branches"`
Types []string `yaml:"types"`
}
type reviewerRouterJob struct {
If string `yaml:"if"`
Steps []reviewerRouterStep `yaml:"steps"`
}
type reviewerRouterStep struct {
Uses string `yaml:"uses"`
With map[string]string `yaml:"with"`
}
func TestReviewerRouterWorkflowContract(t *testing.T) {
t.Parallel()
path, err := filepath.Abs(filepath.Join("..", "..", ".github", "workflows", "reviewer-router.yml"))
if err != nil {
t.Fatalf("Abs(reviewer-router.yml) error = %v", err)
}
data, err := os.ReadFile(path)
if err != nil {
t.Fatalf("ReadFile(%s) error = %v", path, err)
}
var workflow reviewerRouterWorkflow
if err := yaml.Unmarshal(data, &workflow); err != nil {
t.Fatalf("yaml.Unmarshal(%s) error = %v", path, err)
}
policyPath := filepath.Join(filepath.Dir(filepath.Dir(path)), "reviewer-routing.js")
policy, err := os.ReadFile(policyPath)
if err != nil {
t.Fatalf("ReadFile(%s) error = %v", policyPath, err)
}
for _, want := range []string{
"'wxianfeng'",
"'typefield'",
"'haofeng0705'",
"'hlzjsong'",
"resolveReviewRouting",
"unknown_paths",
} {
if !strings.Contains(string(policy), want) {
t.Errorf("reviewer routing policy is missing contract marker %q", want)
}
}
if len(workflow.On) != 1 {
t.Fatalf("workflow triggers = %v, want pull_request_target only", workflow.On)
}
trigger, ok := workflow.On["pull_request_target"]
if !ok {
t.Fatalf("workflow triggers = %v, want pull_request_target", workflow.On)
}
if wantBranches := []string{"main"}; !reflect.DeepEqual(trigger.Branches, wantBranches) {
t.Fatalf("pull_request_target branches = %v, want %v", trigger.Branches, wantBranches)
}
wantTypes := []string{"opened", "synchronize", "reopened", "ready_for_review"}
if !reflect.DeepEqual(trigger.Types, wantTypes) {
t.Fatalf("pull_request_target types = %v, want %v", trigger.Types, wantTypes)
}
wantPermissions := map[string]string{
"contents": "write",
"pull-requests": "write",
}
if !reflect.DeepEqual(workflow.Permissions, wantPermissions) {
t.Fatalf("workflow permissions = %v, want exactly %v", workflow.Permissions, wantPermissions)
}
if len(workflow.Jobs) != 1 {
t.Fatalf("workflow jobs = %v, want one isolated routing job", workflow.Jobs)
}
job, ok := workflow.Jobs["route"]
if !ok {
t.Fatalf("workflow jobs = %v, want route", workflow.Jobs)
}
if job.If != "github.event.pull_request.draft == false" {
t.Fatalf("route.if = %q, want non-draft guard", job.If)
}
const checkoutSHA = "actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683"
const githubScriptSHA = "actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b"
if len(job.Steps) != 2 || job.Steps[0].Uses != checkoutSHA || job.Steps[0].With["ref"] != "${{ github.event.pull_request.base.sha }}" || job.Steps[0].With["persist-credentials"] != "false" || job.Steps[1].Uses != githubScriptSHA {
t.Fatalf("route steps = %#v, want trusted base checkout followed by pinned github-script", job.Steps)
}
script := job.Steps[1].With["script"]
for _, want := range []string{
"REVIEWER_POOL",
"resolveReviewRouting",
"github.rest.pulls.get",
"github.rest.pulls.listFiles",
"currentPull.head.sha !== eventHeadSha",
"currentPull.state !== 'open'",
"currentPull.draft",
"currentPull.base.ref !== 'main'",
"getReadyEventPull('review request')",
"getReadyEventPull('auto-merge enable')",
"context.payload.action === 'synchronize'",
"context.payload.sender?.login?.toLowerCase()",
"reviewer.toLowerCase() !== author",
"reviewer.toLowerCase() !== latestPusher",
"pullRequest.requested_reviewers",
"existingRequestedReviewers",
"github.rest.pulls.listReviews",
"['APPROVED', 'CHANGES_REQUESTED', 'DISMISSED'].includes",
"currentHeadReviewers",
"review.commit_id === headSha",
"['APPROVED', 'CHANGES_REQUESTED'].includes(review.state)",
"review.state === 'CHANGES_REQUESTED'",
"staleChangeRequester",
"state: 'open'",
"loads.set(candidate, loads.get(candidate) + 1)",
"loads.get(left) - loads.get(right)",
"routing.requiredReviewers",
"reviewerCandidates",
"requestReviewersWithFallback",
"satisfiedReviewers",
"github.rest.pulls.requestReviewers",
"trying the next candidate",
"Reviewer routing hit an unexpected error",
"enablePullRequestAutoMerge",
"mergeMethod: MERGE",
"core.warning",
} {
if !strings.Contains(script, want) {
t.Errorf("reviewer router script is missing contract marker %q", want)
}
}
for _, forbidden := range []string{
"['APPROVED', 'CHANGES_REQUESTED', 'COMMENTED']",
"PeterGuy326",
"core.setFailed",
} {
if strings.Contains(string(data), forbidden) {
t.Errorf("reviewer router must not contain %q", forbidden)
}
}
}