Live video on the AT Protocol
1package atproto
2
3import (
4 "context"
5 "encoding/base64"
6 "encoding/json"
7 "os"
8 "path/filepath"
9 "testing"
10
11 "github.com/bluesky-social/indigo/atproto/identity"
12 "github.com/bluesky-social/indigo/xrpc"
13 "github.com/stretchr/testify/require"
14 "stream.place/streamplace/pkg/log"
15 "stream.place/streamplace/pkg/model"
16)
17
18func TestKeyResolution(t *testing.T) {
19 dir, err := os.MkdirTemp("", "atproto-test-*")
20 require.NoError(t, err)
21 defer os.RemoveAll(dir)
22
23 fname := filepath.Join(dir, "db.sqlite")
24 mod, err := model.MakeDB(fname)
25 require.NoError(t, err)
26 oldResolveIdent := ResolveIdent
27 ResolveIdent = func(ctx context.Context, arg string) (*identity.Identity, error) {
28 var doc identity.DIDDocument
29 err = json.Unmarshal(didDoc, &doc)
30 require.NoError(t, err)
31
32 id := identity.ParseIdentity(&doc)
33 return &id, nil
34 }
35 defer func() { ResolveIdent = oldResolveIdent }()
36 oldSyncGetRepo := SyncGetRepo
37 defer func() { SyncGetRepo = oldSyncGetRepo }()
38
39 atsync := &ATProtoSynchronizer{
40 Model: mod,
41 }
42
43 ctx := log.WithDebugValue(context.Background(), map[string]map[string]int{
44 "func": {"handleCreateUpdate": 9, "SyncBlueskyRepo": 9},
45 })
46
47 // full sync
48 SyncGetRepo = MockSyncGetRepo(fullSync)
49 repo, err := atsync.SyncBlueskyRepo(ctx, "streamplace-test", mod)
50 require.NoError(t, err)
51 keys, err := mod.GetSigningKeysForRepo(repo.DID)
52 require.NoError(t, err)
53 require.Len(t, keys, 1)
54 require.Equal(t, firstKey, keys[0].DID)
55
56 // // incremental sync with no changes
57 // SyncGetRepo = MockSyncGetRepo(incrementalSyncSameKey)
58 // repo, err = atsync.SyncBlueskyRepo(context.Background(), "streamplace-test", mod)
59 // require.NoError(t, err)
60 // keys, err = mod.GetSigningKeysForRepo(repo.DID)
61 // require.NoError(t, err)
62 // require.Len(t, keys, 1)
63 // require.Equal(t, firstKey, keys[0].DID)
64
65 // // incremental sync with a new streamplace key
66 // SyncGetRepo = MockSyncGetRepo(incrementalSyncNewKey)
67 // repo, err = atsync.SyncBlueskyRepo(context.Background(), "streamplace-test", mod)
68 // require.NoError(t, err)
69 // keys, err = mod.GetSigningKeysForRepo(repo.DID)
70 // require.NoError(t, err)
71 // require.Len(t, keys, 2)
72 // require.Equal(t, firstKey, keys[0].DID)
73 // require.Equal(t, secondKey, keys[1].DID)
74
75 // // empty sync
76 // SyncGetRepo = MockSyncGetRepo(emptySync)
77 // repo, err = atsync.SyncBlueskyRepo(context.Background(), "streamplace-test", mod)
78 // require.NoError(t, err)
79 // keys, err = mod.GetSigningKeysForRepo(repo.DID)
80 // require.NoError(t, err)
81 // require.Len(t, keys, 2)
82 // require.Equal(t, firstKey, keys[0].DID)
83 // require.Equal(t, secondKey, keys[1].DID)
84}
85
86func MockSyncGetRepo(res string) func(ctx context.Context, xrpcc *xrpc.Client, did string, rev string) ([]byte, error) {
87 return func(ctx context.Context, xrpcc *xrpc.Client, did string, rev string) ([]byte, error) {
88 decoded, err := base64.URLEncoding.DecodeString(res)
89 if err != nil {
90 return nil, err
91 }
92 return decoded, nil
93 }
94}
95
96// captured from plc.directory
97var didDoc = []byte(`
98 {
99 "@context": [
100 "https://www.w3.org/ns/did/v1",
101 "https://w3id.org/security/multikey/v1",
102 "https://w3id.org/security/suites/secp256k1-2019/v1"
103 ],
104 "alsoKnownAs": [
105 "at://streamplace-test.bsky.social"
106 ],
107 "id": "did:plc:ee3n2hs2wkgrkskrz6whzrfs",
108 "service": [
109 {
110 "id": "#atproto_pds",
111 "serviceEndpoint": "https://grisette.us-west.host.bsky.network",
112 "type": "AtprotoPersonalDataServer"
113 }
114 ],
115 "verificationMethod": [
116 {
117 "controller": "did:plc:ee3n2hs2wkgrkskrz6whzrfs",
118 "id": "did:plc:ee3n2hs2wkgrkskrz6whzrfs#atproto",
119 "publicKeyMultibase": "zQ3shYTpdwFJJppCFwncKrB1hSTsKE48s1kTQASKvgSNm3jTt",
120 "type": "Multikey"
121 }
122 ]
123 }
124`)
125var firstKey = "did:key:zQ3shkP7pgLBqp7PYQYnFRSCTBdKAGHWKKghF3GidVmEMUrct"
126var secondKey = "did:key:zQ3shUjidTbELwdHjeNyvx8iQ8adi4789Eaan9Lgc9AUh1Vzm"
127var fullSync = `OqJlcm9vdHOB2CpYJQABcRIgMDc7EDUmj3o8uSuYkHfG46c34xUOpb9gmVTu-M9CbNdndmVyc2lvbgGmAQFxEiCHwNkrZ3dGh8V43q6FFuRg5LlgE50hifxzDmAdATYc6aNlJHR5cGVwcGxhY2Uuc3RyZWFtLmtleWljcmVhdGVkQXR4GDIwMjUtMDEtMjZUMjE6MDc6MzkuMTYxWmpzaWduaW5nS2V5eDlkaWQ6a2V5OnpRM3Noa1A3cGdMQnFwN1BZUVluRlJTQ1RCZEtBR0hXS0tnaEYzR2lkVm1FTVVyY3T4AQFxEiBzaFlz2gS4XkDfdJHIjomgqdCpk7Cc49rX3kvYqLP6eaJhZYKkYWtYG2FwcC5ic2t5LmFjdG9yLnByb2ZpbGUvc2VsZmFwAGF02CpYJQABcRIgVDDzvM6e19so-zvPpJEldfpzAbpZZ6WxE81LAYvjCv1hdtgqWCUAAXESIMzJIP4DaRsw1NyMu1Su2ggAXMDz0nOK13rNPkQz00L0pGFrWB5wbGFjZS5zdHJlYW0ua2V5LzNsZ29kZ3Q2bzY1MjRhcABhdPZhdtgqWCUAAXESIIfA2Stnd0aHxXjeroUW5GDkuWATnSGJ_HMOYB0BNhzpYWz24AEBcRIgMDc7EDUmj3o8uSuYkHfG46c34xUOpb9gmVTu-M9CbNemY2RpZHggZGlkOnBsYzplZTNuMmhzMndrZ3Jrc2tyejZ3aHpyZnNjcmV2bTNsZ29kZ3Q2dHpuMjRjc2lnWEDo6aLAaagS8QCgO_fuXpYApipc9VpFI4mmOPpmIIKWyHQnXiMurYyqNs0oGX31lXUmBlNQq0mUDKY8D2FbxSyaZGRhdGHYKlglAAFxEiBzaFlz2gS4XkDfdJHIjomgqdCpk7Cc49rX3kvYqLP6eWRwcmV29md2ZXJzaW9uA84BAXESIMzJIP4DaRsw1NyMu1Su2ggAXMDz0nOK13rNPkQz00L0pGUkdHlwZXZhcHAuYnNreS5hY3Rvci5wcm9maWxlZmF2YXRhcqRjcmVm2CpYJQABVRIgHdCQPIz2IErjjii59bHG-XOJjtGVJLiR1RRnLKsXK3xkc2l6ZRl3ZmUkdHlwZWRibG9iaG1pbWVUeXBlaWltYWdlL3BuZ2ljcmVhdGVkQXR4GDIwMjUtMDEtMjZUMjE6MDQ6MzUuNjMxWmtkaXNwbGF5TmFtZWCEAQFxEiBUMPO8zp7X2yj7O8-kkSV1-nMBullnpbETzUsBi-MK_aJhZYGkYWtYI2FwcC5ic2t5LmdyYXBoLmZvbGxvdy8zbGdvZGJkM3poYzJoYXAAYXT2YXbYKlglAAFxEiAOO157S10FbyFkhacvqpz-lvrDDv1i9kTTw7p26_NFX2Fs9o8BAXESIA47XntLXQVvIWSFpy-qnP6W-sMO_WL2RNPDunbr80Vfo2UkdHlwZXVhcHAuYnNreS5ncmFwaC5mb2xsb3dnc3ViamVjdHggZGlkOnBsYzp6NzJpN2hkeW5tazZyMjJ6MjdoNnR2dXJpY3JlYXRlZEF0eBgyMDI1LTAxLTI2VDIxOjA0OjM0LjcxM1o=`
128var incrementalSyncSameKey = `OqJlcm9vdHOB2CpYJQABcRIgKRCUWJjPxaq1aqO7srwRxpJeURCsoQ_sXJaSkyscKXRndmVyc2lvbgHRAQFxEiD8HmkbON-0uUXvBD5PHbTu9hfJYYh7BcQBBbua6OlXNqJhZYKkYWtYIGFwcC5ic2t5LmZlZWQucG9zdC8zbGdvZGx3eGg0dDJoYXAAYXT2YXbYKlglAAFxEiAJRIEWBgnKqUlv19Ese8UO557t5YL7jfcEBItUpEm9IqRha1gaZ3JhcGguZm9sbG93LzNsZ29kYmQzemhjMmhhcAlhdPZhdtgqWCUAAXESIA47XntLXQVvIWSFpy-qnP6W-sMO_WL2RNPDunbr80VfYWz2-AEBcRIguxQZbYxqjerYzPJzsB2cq47_Cy-M9DbdfX3zgsZsHSKiYWWCpGFrWBthcHAuYnNreS5hY3Rvci5wcm9maWxlL3NlbGZhcABhdNgqWCUAAXESIPweaRs437S5Re8EPk8dtO72F8lhiHsFxAEFu5ro6Vc2YXbYKlglAAFxEiDMySD-A2kbMNTcjLtUrtoIAFzA89Jzitd6zT5EM9NC9KRha1gecGxhY2Uuc3RyZWFtLmtleS8zbGdvZGd0Nm82NTI0YXAAYXT2YXbYKlglAAFxEiCHwNkrZ3dGh8V43q6FFuRg5LlgE50hifxzDmAdATYc6WFs9uABAXESICkQlFiYz8WqtWqju7K8EcaSXlEQrKEP7FyWkpMrHCl0pmNkaWR4IGRpZDpwbGM6ZWUzbjJoczJ3a2dya3Nrcno2d2h6cmZzY3Jldm0zbGdvZGx3eXhkeDJqY3NpZ1hAokbAmjkgNdLlufRil-TeFuxKggit3Ljqkbw7QKRY794EPZYnb1WQE6sPhcF7Z0VNTl05APJPEb0s4SKM8qloRGRkYXRh2CpYJQABcRIguxQZbYxqjerYzPJzsB2cq47_Cy-M9DbdfX3zgsZsHSJkcHJldvZndmVyc2lvbgOIAQFxEiAJRIEWBgnKqUlv19Ese8UO557t5YL7jfcEBItUpEm9IqRkdGV4dHZIaSB0aGlzIGlzIGEgdGVzdCBwb3N0ZSR0eXBlcmFwcC5ic2t5LmZlZWQucG9zdGVsYW5nc4FiZW5pY3JlYXRlZEF0eBgyMDI1LTAxLTI2VDIxOjEwOjMxLjA4MFo=`
129var incrementalSyncNewKey = `OqJlcm9vdHOB2CpYJQABcRIglIEJIf3uyrHn7zpaDG0VZ5LFVpEnAO4COXPLdZVI5N5ndmVyc2lvbgGmAQFxEiC7UEFQdgwPLi4Aph-E0JNXfiJhpy7EK7yIae9mt-JLlqNlJHR5cGVwcGxhY2Uuc3RyZWFtLmtleWljcmVhdGVkQXR4GDIwMjUtMDEtMjZUMjE6MTA6NTkuMDI5WmpzaWduaW5nS2V5eDlkaWQ6a2V5OnpRM3NoVWppZFRiRUx3ZEhqZU55dng4aVE4YWRpNDc4OUVhYW45TGdjOUFVaDFWem3gAQFxEiCUgQkh_e7KsefvOloMbRVnksVWkScA7gI5c8t1lUjk3qZjZGlkeCBkaWQ6cGxjOmVlM24yaHMyd2tncmtza3J6NndoenJmc2NyZXZtM2xnb2RtcnJqb3EydGNzaWdYQPoUwYR8ZFqHPygnPU0Yzo14pRo35huNbjVwtQudrSIMErQMHsGhZL-JdLnq5avgxXYqq7k4jcMUEkUIgmZFI51kZGF0YdgqWCUAAXESIEBLuaWzxeuRKjP8Kovw6ZWkkvqfMkhDOBymBuE4ZZhRZHByZXb2Z3ZlcnNpb24DtQIBcRIgQEu5pbPF65EqM_wqi_DplaSS-p8ySEM4HKYG4ThlmFGiYWWDpGFrWBthcHAuYnNreS5hY3Rvci5wcm9maWxlL3NlbGZhcABhdNgqWCUAAXESIPweaRs437S5Re8EPk8dtO72F8lhiHsFxAEFu5ro6Vc2YXbYKlglAAFxEiDMySD-A2kbMNTcjLtUrtoIAFzA89Jzitd6zT5EM9NC9KRha1gecGxhY2Uuc3RyZWFtLmtleS8zbGdvZGd0Nm82NTI0YXAAYXT2YXbYKlglAAFxEiCHwNkrZ3dGh8V43q6FFuRg5LlgE50hifxzDmAdATYc6aRha0htcnJmcnEydGFwFmF09mF22CpYJQABcRIgu1BBUHYMDy4uAKYfhNCTV34iYacuxCu8iGnvZrfiS5ZhbPY=`
130var emptySync = `OqJlcm9vdHOB2CpYJQABcRIglIEJIf3uyrHn7zpaDG0VZ5LFVpEnAO4COXPLdZVI5N5ndmVyc2lvbgE=`