-
Notifications
You must be signed in to change notification settings - Fork 2
/
example_test.go
184 lines (154 loc) · 6.96 KB
/
example_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
// Test linkstore assuming a CID LinkSystem.
package linkstore
import (
"context"
"os"
"path"
"testing"
"github.com/ipfs/go-cid"
// IPLD Imports:
carv1 "github.com/ipld/go-car"
carv2 "github.com/ipld/go-car/v2"
"github.com/ipld/go-ipld-prime"
"github.com/ipld/go-ipld-prime/datamodel"
"github.com/ipld/go-ipld-prime/fluent"
"github.com/ipld/go-ipld-prime/linking"
"github.com/ipld/go-ipld-prime/linking/cid"
"github.com/ipld/go-ipld-prime/node/basicnode"
"github.com/ipld/go-ipld-prime/storage"
"github.com/ipld/go-ipld-prime/traversal/selector"
sbuilder "github.com/ipld/go-ipld-prime/traversal/selector/builder"
"github.com/multiformats/go-multicodec"
// Note: this dagcbor import populates the multicodec table as a side effect.
// The CID LinkSystem requires this to function. See:
// https://github.com/ipld/go-ipld-prime/blob/fc47eb2f400c1ab39da4df91c4e03f04e34e26cb/linkingExamples_test.go#L58
_ "github.com/ipld/go-ipld-prime/codec/dagcbor"
. "github.com/warpfork/go-wish"
)
func TestMain(m *testing.M) {
failedTestCount := m.Run()
os.Exit(failedTestCount)
}
func TestWritingCarsWithStorageLinkSystem(t *testing.T) {
var testBlockReadOpener ipld.BlockReadOpener
var testBlockWriteOpener ipld.BlockWriteOpener
var testLinkSystem linking.LinkSystem
// Filename for carfile wirting tests. Will end up begin written as, eg,
// carfile.v1.car, carfile.v2.car, ie, with version and ".car" appended.
// We don't use os.CreateTemp here, for consistency, because the carfile
// v2 API handles file creation.
fileprefix := "carfile"
// Note: os.TempDir() doesn't guarantee existence or writability of the
// returned location. This might cause tests to fail. TODO: Check.
v1carfilename := path.Join(os.TempDir(), fileprefix+".v1"+".car")
v2carfilename := path.Join(os.TempDir(), fileprefix+".v2"+".car")
var root datamodel.Link
t.Run("Writing v1 carfile and wrapping to v2.", func(t *testing.T) {
// Create our store link system, the system under test, and make some
// assertions about its initialized state.
storageLinkSystem := NewStorageLinkSystemWithNewStorage(cidlink.DefaultLinkSystem())
Wish(t, storageLinkSystem.ReadStore, ShouldBeSameTypeAs, readStore(testBlockReadOpener))
Wish(t, storageLinkSystem.WriteStore, ShouldBeSameTypeAs, writeStore(testBlockWriteOpener))
Wish(t, storageLinkSystem.LinkSystem, ShouldBeSameTypeAs, testLinkSystem)
// Build a trivial "hello world" node.
n := fluent.MustBuildMap(basicnode.Prototype.Map, 1, func(na fluent.MapAssembler) {
na.AssembleEntry("hello").AssignString("world")
})
// Reasonable defaults for our CID LinkSystem (kubelt compatible).
lp := cidlink.LinkPrototype{cid.Prefix{
Version: 1,
Codec: uint64(multicodec.DagCbor), //0x71,
MhType: uint64(multicodec.Sha2_256), //0x12,
MhLength: 32, // sha2-256 hash has a 32-byte sum.
}}
// Store the test node into the link system's embedded memory store.
root = storageLinkSystem.MustStore(linking.LinkContext{}, lp, n)
// Create a selective car (that selects everything) from the LinkSystem.
sc := carv1.NewSelectiveCar(context.Background(),
storageLinkSystem.ReadStore, // <- special sauce
[]carv1.Dag{{
// CID of the root node of the DAG to traverse.
Root: root.(cidlink.Link).Cid,
// Traversal convenience selector that gives us "everything".
Selector: everything(),
}})
// Create the file on disk that will store the car representation.
carfile_v1, err := os.Create(v1carfilename)
Wish(t, err, ShouldEqual, nil)
// Write the selective carfile from the memory store to disk.
sc.Write(carfile_v1)
// Cheat to create a carv2 file...
// A carv2 file is a carv1 file with a prepended header and appended index.
// We "wrap" the carv1 file we just made to turn it into a carv2 file.
// This is done for convenience. Currently carv1 is used directly by the
// `ipfs dag import <file>` command, but carv2 is the future. There are more
// verbose ways of creating carv2 files we're not going to delve into yet.
err = carv2.WrapV1File(v1carfilename, v2carfilename)
Wish(t, err, ShouldEqual, nil)
})
t.Run("Reading v1 carfile.", func(t *testing.T) {
// Make a new memory store so we're not loading from cache. Overwrite
// the link system's storage handlers to point at the new store. For the
// sake of exercising the library we use ConfigureStorage to set us up.
storageLinkSystem := NewStorageLinkSystemWithNoStorage(cidlink.DefaultLinkSystem())
var fresh_store = storage.Memory{}
storageLinkSystem.ConfigureStorage(fresh_store)
// Different init method, same assertions as above.
Wish(t, storageLinkSystem.ReadStore, ShouldBeSameTypeAs, readStore(testBlockReadOpener))
Wish(t, storageLinkSystem.WriteStore, ShouldBeSameTypeAs, writeStore(testBlockWriteOpener))
Wish(t, storageLinkSystem.LinkSystem, ShouldBeSameTypeAs, testLinkSystem)
// Open the carfile (os.File) so we can read it in.
read_carfile_v1, err := os.Open(v1carfilename)
Wish(t, err, ShouldEqual, nil)
// Now restore the blocks in the link system from disk.
_, err = carv1.LoadCar(storageLinkSystem.WriteStore, read_carfile_v1)
Wish(t, err, ShouldEqual, nil)
// Load the fluent model from the link system.
node_from_carfile, err := storageLinkSystem.Load(
ipld.LinkContext{},
root,
basicnode.Prototype.Any,
)
Wish(t, err, ShouldEqual, nil)
// Retrieve our hello -> "world" message.
world_node, err := node_from_carfile.LookupByString("hello")
Wish(t, err, ShouldEqual, nil)
world, err := world_node.AsString()
Wish(t, err, ShouldEqual, nil)
Wish(t, world, ShouldEqual, "world")
})
t.Run("Deleting v1 carfile.", func(t *testing.T) {
err := os.Remove(v1carfilename)
Wish(t, err, ShouldEqual, nil)
})
t.Run("Reading v2 carfile.", func(t *testing.T) {
cr, err := carv2.OpenReader(v2carfilename)
Wish(t, err, ShouldEqual, nil)
defer func() {
// Feels weird to defer a Wish here, so leaving the panic.
if err := cr.Close(); err != nil {
panic(err)
}
}()
// Do we get back the same root we saved (after type coercsion)?
roots, err := cr.Roots()
Wish(t, err, ShouldEqual, nil)
Wish(t, roots[0], ShouldBeSameTypeAs, root.(cidlink.Link).Cid)
Wish(t, roots[0], ShouldEqual, root.(cidlink.Link).Cid)
})
t.Run("Deleting v2 carfile.", func(t *testing.T) {
err := os.Remove(v2carfilename)
Wish(t, err, ShouldEqual, nil)
})
}
// This block makes a selector that recurses over the fluent reflection of
// our go struct (ipld map) and gives us "everything". It's a rip job from
// https://github.com/ipld/go-ipld-adl-hamt/issues/24, see also:
// https://github.com/ipld/go-ipld-prime/issues/171, and:
// https://github.com/ipld/go-ipld-prime/pull/199/files
func everything() datamodel.Node {
ssb := sbuilder.NewSelectorSpecBuilder(basicnode.Prototype.Any)
return ssb.ExploreFields(func(efsb sbuilder.ExploreFieldsSpecBuilder) {
efsb.Insert("Links", ssb.ExploreIndex(1, ssb.ExploreRecursive(selector.RecursionLimitNone(), ssb.ExploreAll(ssb.ExploreRecursiveEdge()))))
}).Node()
}