Files
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

208 lines
5.5 KiB
Go

package authenticated_test
import (
"context"
"io"
"testing"
"time"
authenticated "github.com/tutti-os/tutti/packages/device-link/authenticated"
)
func TestAuthenticatedParticipantsCarryBidirectionalStream(t *testing.T) {
t.Parallel()
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
defer cancel()
caller, err := authenticated.NewParticipant(authenticated.ParticipantConfig{IncludeLoopback: true})
if err != nil {
t.Fatal(err)
}
defer caller.Close()
owner, err := authenticated.NewParticipant(authenticated.ParticipantConfig{IncludeLoopback: true})
if err != nil {
t.Fatal(err)
}
defer owner.Close()
callerDescription, err := caller.LocalDescription(ctx)
if err != nil {
t.Fatal(err)
}
ownerDescription, err := owner.LocalDescription(ctx)
if err != nil {
t.Fatal(err)
}
ownerResult := make(chan linkResult, 1)
go func() {
link, connectErr := owner.Connect(ctx, callerDescription, authenticated.RoleOwner)
ownerResult <- linkResult{link: link, err: connectErr}
}()
callerLink, err := caller.Connect(ctx, ownerDescription, authenticated.RoleCaller)
if err != nil {
t.Fatal(err)
}
defer callerLink.Close()
result := <-ownerResult
if result.err != nil {
t.Fatal(result.err)
}
defer result.link.Close()
serveDone := make(chan error, 1)
go func() {
stream, acceptErr := result.link.AcceptStream(ctx)
if acceptErr != nil {
serveDone <- acceptErr
return
}
defer stream.Close()
_, copyErr := io.Copy(stream, stream)
serveDone <- copyErr
}()
stream, err := callerLink.OpenStream(ctx)
if err != nil {
t.Fatal(err)
}
payload := []byte("authenticated-device-link")
if _, err := stream.Write(payload); err != nil {
t.Fatal(err)
}
got := make([]byte, len(payload))
if _, err := io.ReadFull(stream, got); err != nil {
t.Fatal(err)
}
if string(got) != string(payload) {
t.Fatalf("echo = %q, want %q", got, payload)
}
if err := stream.Close(); err != nil {
t.Fatal(err)
}
if err := <-serveDone; err != nil && err != io.EOF {
t.Fatal(err)
}
}
func TestAuthenticatedParticipantRejectsInvalidPeerBeforeConnecting(t *testing.T) {
t.Parallel()
participant, err := authenticated.NewParticipant(authenticated.ParticipantConfig{IncludeLoopback: true})
if err != nil {
t.Fatal(err)
}
defer participant.Close()
_, err = participant.Connect(context.Background(), authenticated.Description{
Fingerprint: "invalid", Ufrag: "ufrag", Pwd: "pwd", Candidates: []string{"candidate"},
}, authenticated.RoleCaller)
if err == nil {
t.Fatal("Connect succeeded with invalid peer fingerprint")
}
}
func TestAuthenticatedParticipantsAcceptCredentialsBeforeCandidateTrickle(t *testing.T) {
t.Parallel()
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
defer cancel()
caller, err := authenticated.NewParticipant(authenticated.ParticipantConfig{IncludeLoopback: true})
if err != nil {
t.Fatal(err)
}
defer caller.Close()
owner, err := authenticated.NewParticipant(authenticated.ParticipantConfig{IncludeLoopback: true})
if err != nil {
t.Fatal(err)
}
defer owner.Close()
callerInitial, err := caller.StartLocalDescription()
if err != nil {
t.Fatal(err)
}
ownerInitial, err := owner.StartLocalDescription()
if err != nil {
t.Fatal(err)
}
if caller.LocalCandidateChanges() == nil || owner.LocalGatheringComplete() == nil {
t.Fatal("authenticated trickle signals are unavailable")
}
callerFull, err := caller.LocalDescription(ctx)
if err != nil {
t.Fatal(err)
}
ownerFull, err := owner.LocalDescription(ctx)
if err != nil {
t.Fatal(err)
}
callerInitial.Candidates = nil
ownerInitial.Candidates = nil
callerResult := make(chan linkResult, 1)
ownerResult := make(chan linkResult, 1)
go func() {
link, connectErr := caller.Connect(ctx, ownerInitial, authenticated.RoleCaller)
callerResult <- linkResult{link: link, err: connectErr}
}()
go func() {
link, connectErr := owner.Connect(ctx, callerInitial, authenticated.RoleOwner)
ownerResult <- linkResult{link: link, err: connectErr}
}()
time.Sleep(50 * time.Millisecond)
if added := caller.AddRemoteCandidates(ownerFull.Candidates); added == 0 {
t.Fatal("caller accepted no trickled owner candidates")
}
if added := owner.AddRemoteCandidates(callerFull.Candidates); added == 0 {
t.Fatal("owner accepted no trickled caller candidates")
}
callerConnected, ownerConnected := <-callerResult, <-ownerResult
if callerConnected.err != nil {
t.Fatalf("caller Connect after trickle: %v", callerConnected.err)
}
if ownerConnected.err != nil {
t.Fatalf("owner Connect after trickle: %v", ownerConnected.err)
}
defer callerConnected.link.Close()
defer ownerConnected.link.Close()
}
func TestAuthenticatedParticipantRejectsInvalidCompatibleProtocol(t *testing.T) {
t.Parallel()
_, err := authenticated.NewParticipant(authenticated.ParticipantConfig{
IncludeLoopback: true,
CompatibleProtocols: []string{" "},
})
if err == nil {
t.Fatal("NewParticipant accepted an empty compatible protocol")
}
}
func TestAuthenticatedParticipantDoneRemainsObservableAfterClose(t *testing.T) {
t.Parallel()
participant, err := authenticated.NewParticipant(authenticated.ParticipantConfig{IncludeLoopback: true})
if err != nil {
t.Fatal(err)
}
done := participant.Done()
if done == nil {
t.Fatal("Done returned a nil channel")
}
if err := participant.Close(); err != nil {
t.Fatal(err)
}
if participant.Done() != done {
t.Fatal("Done channel changed after Close")
}
select {
case <-done:
case <-time.After(time.Second):
t.Fatal("Done did not close")
}
}
type linkResult struct {
link *authenticated.Link
err error
}