Files
mesh-catalog/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/bridge_test.go
T

202 lines
5.3 KiB
Go

package main
import (
"errors"
"sort"
"strconv"
"strings"
"sync"
sdk "git.novox.be/novox/mesh-sdk/go/delivery"
)
// fakeMerges is the gate's bucket merges with the forge bridge behind it. Every pull request the gate writes in the state
// merging is merged on the fake forge, as the bridge's merge does (mesh-delivery modules/mesh-delivery-forge-bridge
// internal/bridge acts.go MergeActs): the forge's merge tool is asked at the head with the approved heads of the whole
// group, `<repository>@<full sha>` sorted, and what the fake forge answers is written on FORGE as the bridge writes it, a
// merged message or a refused one with the forge's words. With manual set the bridge says nothing.
type fakeMerges struct {
mu sync.Mutex
forge *fakeForge
h *Holder
values map[string]sdk.Merge
puts []sdk.Merge
manual bool
failed error
// said are the messages the bridge wrote on FORGE, in order.
said []sdk.Forge
}
func newFakeMerges(f *fakeForge) *fakeMerges {
return &fakeMerges{forge: f, values: map[string]sdk.Merge{}}
}
func (m *fakeMerges) Get(group string) (*sdk.Merge, error) {
m.mu.Lock()
defer m.mu.Unlock()
if v, ok := m.values[group]; ok {
return &v, nil
}
return nil, nil
}
func (m *fakeMerges) Put(group string, v sdk.Merge) error {
if _, err := sdk.EncodeMerge(v); err != nil {
return err
}
m.mu.Lock()
if m.failed != nil {
defer m.mu.Unlock()
return m.failed
}
m.values[group] = v
m.puts = append(m.puts, v)
manual := m.manual
m.mu.Unlock()
if manual {
return nil
}
for _, p := range v.Pulls {
if p.State != sdk.MergeMerging {
continue
}
owner, repo, _ := strings.Cut(p.Repository, "/")
var approved []string
for key, sha := range v.Approved {
repository, _, _ := strings.Cut(key, "@")
approved = append(approved, repository+"@"+sha)
}
sortStrings(approved)
f := m.forge
f.mu.Lock()
f.merges = append(f.merges, owner+"/"+repo+"#"+itoa(p.Number)+"@"+p.Head.Head)
f.approvedHeads = append(f.approvedHeads, approved)
err := f.refuseRepo[owner+"/"+repo]
if err == nil {
err = f.refuse
}
f.mu.Unlock()
msg := sdk.Forge{Repository: p.Repository, Number: p.Number, Head: p.Head.Head}
if err != nil {
msg.Kind, msg.Act, msg.Words = sdk.ForgeRefused, "merge", err.Error()
} else {
msg.Kind, msg.Merge, msg.Root = sdk.ForgeMerged, strings.Repeat("c", 40), "root"
msg.Trees = map[string]string{"app": "tree"}
}
m.say(msg)
}
return nil
}
// say writes a message on FORGE and has the gate hear it, as the runtime does.
func (m *fakeMerges) say(msg sdk.Forge) {
body, err := sdk.EncodeForge(msg)
if err != nil {
panic(err)
}
event, err := sdk.ForgeEvent(msg.Repository, msg.Kind)
if err != nil {
panic(err)
}
m.mu.Lock()
m.said = append(m.said, msg)
m.mu.Unlock()
m.h.Forged("mesh-delivery-forge-bridge."+event, body)
}
// last is the newest value written for a group.
func (m *fakeMerges) last(group string) sdk.Merge {
m.mu.Lock()
defer m.mu.Unlock()
return m.values[group]
}
// fakeSource is the gate's bucket source with its one key.
type fakeSource struct {
mu sync.Mutex
main *sdk.Source
moves []sdk.Source
}
func (s *fakeSource) Move(update func(prev *sdk.Source) (sdk.Source, bool)) error {
s.mu.Lock()
defer s.mu.Unlock()
next, write := update(s.main)
if !write {
return nil
}
if _, err := sdk.EncodeSource(next); err != nil {
return err
}
s.main = &next
s.moves = append(s.moves, next)
return nil
}
func sortStrings(s []string) { sort.Strings(s) }
func itoa(n int) string { return strconv.Itoa(n) }
// fakeOwed is the gate's bucket owed with the forge bridge behind it: every act written is performed on the fake forge, as the
// bridge performs it (mesh-delivery modules/mesh-delivery-forge-bridge internal/bridge acts.go OwedAct), once for each
// revision. With down set the bucket cannot be written (the bus is away); the forge's own refusals are the bridge's to say,
// not the gate's to retry.
type fakeOwed struct {
mu sync.Mutex
forge *fakeForge
down bool
// keys are the latest value of each key written, and writes every revision in order.
keys map[string]sdk.Owed
writes []string
}
func newFakeOwed(f *fakeForge) *fakeOwed { return &fakeOwed{forge: f, keys: map[string]sdk.Owed{}} }
func (o *fakeOwed) Put(key string, v sdk.Owed) error {
if _, err := sdk.EncodeOwed(v); err != nil {
return err
}
repository, number, head, act, err := sdk.ParseOwedKey(key)
if err != nil {
return err
}
o.mu.Lock()
if o.down {
defer o.mu.Unlock()
return errors.New("the bus is away")
}
if kept, ok := o.keys[key]; ok && sdk.SameOwed(kept, v) {
o.mu.Unlock()
return nil
}
o.keys[key] = v
o.writes = append(o.writes, key)
o.mu.Unlock()
owner, repo, _ := strings.Cut(repository, "/")
f := o.forge
switch v.Act {
case sdk.OwedNote:
_ = f.Note(owner, repo, head, v.Text)
case sdk.OwedView:
_ = f.View(owner, repo, number, v.Text)
case sdk.OwedStatus:
_ = f.Status(owner, repo, head, v.Context, v.State, v.Text, v.Target)
case sdk.OwedComment:
_ = f.Comment(owner, repo, number, v.Text)
}
_ = act
return nil
}
// acts are the keys written whose act is of a kind, in order.
func (o *fakeOwed) acts(kind string) []string {
o.mu.Lock()
defer o.mu.Unlock()
var out []string
for _, k := range o.writes {
if _, _, _, act, err := sdk.ParseOwedKey(k); err == nil && actKind(act) == kind {
out = append(out, k)
}
}
return out
}