1 | var test = require('tape')
|
2 | var create = require('./helpers/create')
|
3 |
|
4 | test('basic symlink', t => {
|
5 | const archive = create()
|
6 |
|
7 | archive.writeFile('/hello.txt', 'world', err => {
|
8 | t.error(err, 'no error')
|
9 | archive.symlink('/hello.txt', '/link.txt', err => {
|
10 | t.error(err, 'no error')
|
11 | onlink()
|
12 | })
|
13 | })
|
14 |
|
15 | function onlink () {
|
16 | archive.stat('/link.txt', (err, st) => {
|
17 | t.error(err, 'no error')
|
18 | t.same(st.size, 5)
|
19 | archive.readFile('/link.txt', (err, contents) => {
|
20 | t.error(err, 'no error')
|
21 | t.same(contents, Buffer.from('world'))
|
22 | t.end()
|
23 | })
|
24 | })
|
25 | }
|
26 | })
|
27 |
|
28 | test('fixing a broken symlink', t => {
|
29 | const archive = create()
|
30 |
|
31 | archive.symlink('/hello.txt', '/link.txt', err => {
|
32 | t.error(err, 'no error')
|
33 | archive.stat('/link.txt', (err, st) => {
|
34 | t.same(err.errno, 2)
|
35 | archive.writeFile('/hello.txt', 'world', err => {
|
36 | t.error(err, 'no error')
|
37 | onwrite()
|
38 | })
|
39 | })
|
40 | })
|
41 |
|
42 | function onwrite () {
|
43 | archive.stat('/link.txt', (err, st) => {
|
44 | t.error(err, 'no error')
|
45 | t.same(st.size, 5)
|
46 | archive.readFile('/link.txt', (err, contents) => {
|
47 | t.error(err, 'no error')
|
48 | t.same(contents, Buffer.from('world'))
|
49 | t.end()
|
50 | })
|
51 | })
|
52 | }
|
53 | })
|
54 |
|
55 | test('unlinking a symlink does not delete the target', t => {
|
56 | const archive = create()
|
57 |
|
58 | archive.writeFile('/hello.txt', 'world', err => {
|
59 | t.error(err, 'no error')
|
60 | archive.symlink('/hello.txt', '/link.txt', err => {
|
61 | t.error(err, 'no error')
|
62 | archive.unlink('/link.txt', err => {
|
63 | t.error(err, 'no error')
|
64 | onunlink()
|
65 | })
|
66 | })
|
67 | })
|
68 |
|
69 | function onunlink () {
|
70 | archive.stat('/hello.txt', (err, st) => {
|
71 | t.error(err, 'no error')
|
72 | t.same(st.size, 5)
|
73 | archive.readFile('/hello.txt', (err, contents) => {
|
74 | t.error(err, 'no error')
|
75 | t.same(contents, Buffer.from('world'))
|
76 | t.end()
|
77 | })
|
78 | })
|
79 | }
|
80 | })
|
81 |
|
82 | test('symlinks appear in readdir', t => {
|
83 | const archive = create()
|
84 |
|
85 | archive.writeFile('/hello.txt', 'world', err => {
|
86 | t.error(err, 'no error')
|
87 | archive.symlink('/hello.txt', '/link.txt', err => {
|
88 | t.error(err, 'no error')
|
89 | onlink()
|
90 | })
|
91 | })
|
92 |
|
93 | function onlink () {
|
94 | archive.readdir('/', (err, files) => {
|
95 | t.error(err, 'no errors')
|
96 | t.same(files, ['hello.txt', 'link.txt'])
|
97 | t.end()
|
98 | })
|
99 | }
|
100 | })
|
101 |
|
102 | test('symlinks with nested symlinks appear in non-recursive readdir', t => {
|
103 | const drive = create()
|
104 |
|
105 | drive.mkdir('a', err => {
|
106 | t.error(err)
|
107 | drive.writeFile('a/1', '1', err => {
|
108 | t.error(err, 'no error')
|
109 | drive.writeFile('a/2', '2', err => {
|
110 | t.error(err, 'no error')
|
111 | drive.symlink('a/2', 'a/3', err => {
|
112 | t.error(err, 'no error')
|
113 | drive.symlink('a', 'b', err => {
|
114 | t.error(err, 'no error')
|
115 | onlink()
|
116 | })
|
117 | })
|
118 | })
|
119 | })
|
120 | })
|
121 |
|
122 | function onlink () {
|
123 | drive.readdir('b', (err, files) => {
|
124 | t.error(err, 'no error')
|
125 | t.same(files, ['3', '1', '2'])
|
126 | t.end()
|
127 | })
|
128 | }
|
129 | })
|
130 |
|
131 | test('nested, broken symlink without parent stats can be removed', t => {
|
132 | const drive = create()
|
133 |
|
134 | drive.symlink('bad-target', 'a/b/c/d', err => {
|
135 | t.error(err)
|
136 | drive.lstat('a/b/c/d', (err, st) => {
|
137 | t.error(err, 'no error')
|
138 | t.true(st)
|
139 | drive.unlink('a/b/c/d', err => {
|
140 | t.error(err, 'no error')
|
141 | drive.readdir('a/b/c', (err, l) => {
|
142 | t.error(err, 'no error')
|
143 | t.same(l.length, 0)
|
144 | t.end()
|
145 | })
|
146 | })
|
147 | })
|
148 | })
|
149 | })
|
150 |
|
151 | test('readdir with includeStats returns unresolved stats', t => {
|
152 | const drive = create()
|
153 |
|
154 | drive.writeFile('/hello', 'world', { metadata: { a: 'bbb' }}, err => {
|
155 | t.error(err, 'no error')
|
156 | drive.symlink('/hello', '/link', err => {
|
157 | t.error(err, 'no error')
|
158 | drive.readdir('/', { includeStats: true }, (err, list) => {
|
159 | t.error(err, 'no error')
|
160 | t.same(list.length, 2)
|
161 | for (const { stat } of list) {
|
162 | if (stat.metadata.a) t.true(stat.metadata.a.equals(Buffer.from('bbb')))
|
163 | else t.same(stat.linkname, '/hello')
|
164 | }
|
165 | t.end()
|
166 | })
|
167 | })
|
168 | })
|
169 | })
|
170 |
|
171 |
|
172 | test.skip('stat through symlinked dir', t => {
|
173 | const drive = create()
|
174 |
|
175 | drive.mkdir('subdir1', err => {
|
176 | t.error(err, 'no error')
|
177 | drive.mkdir('subdir2', err => {
|
178 | t.error(err, 'no error')
|
179 | drive.writeFile('subdir1/foo', 'hello', err => {
|
180 | t.error(err, 'no error')
|
181 | drive.writeFile('subdir2/bar', 'world', err => {
|
182 | t.error(err, 'no error')
|
183 | drive.symlink('/subdir2', 'subdir1/symto2', err => {
|
184 | t.error(err, 'no error')
|
185 | return onlinked()
|
186 | })
|
187 | })
|
188 | })
|
189 | })
|
190 | })
|
191 |
|
192 | function onlinked () {
|
193 | drive.stat('subdir1/symto2/bar', (err, st) => {
|
194 | t.error(err, 'no error')
|
195 | t.true(st)
|
196 | t.end()
|
197 | })
|
198 | }
|
199 | })
|
200 |
|
201 |
|