github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/client/snapshot_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2018 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package client_test 21 22 import ( 23 "io/ioutil" 24 "net/http" 25 "net/url" 26 "strconv" 27 "strings" 28 "time" 29 30 "gopkg.in/check.v1" 31 32 "github.com/snapcore/snapd/client" 33 "github.com/snapcore/snapd/snap" 34 ) 35 36 func (cs *clientSuite) TestClientSnapshotIsValid(c *check.C) { 37 now := time.Now() 38 revno := snap.R(1) 39 sums := map[string]string{"user/foo.tgz": "some long hash"} 40 c.Check((&client.Snapshot{ 41 SetID: 42, 42 Time: now, 43 Snap: "asnap", 44 Revision: revno, 45 SHA3_384: sums, 46 }).IsValid(), check.Equals, true) 47 48 for desc, snapshot := range map[string]*client.Snapshot{ 49 "nil": nil, 50 "empty": {}, 51 "no id": { /*SetID: 42,*/ Time: now, Snap: "asnap", Revision: revno, SHA3_384: sums}, 52 "no time": {SetID: 42 /*Time: now,*/, Snap: "asnap", Revision: revno, SHA3_384: sums}, 53 "no snap": {SetID: 42, Time: now /*Snap: "asnap",*/, Revision: revno, SHA3_384: sums}, 54 "no rev": {SetID: 42, Time: now, Snap: "asnap" /*Revision: revno,*/, SHA3_384: sums}, 55 "no sums": {SetID: 42, Time: now, Snap: "asnap", Revision: revno /*SHA3_384: sums*/}, 56 } { 57 c.Check(snapshot.IsValid(), check.Equals, false, check.Commentf("%s", desc)) 58 } 59 60 } 61 62 func (cs *clientSuite) TestClientSnapshotSetTime(c *check.C) { 63 // if set is empty, it doesn't explode (and returns the zero time) 64 c.Check(client.SnapshotSet{}.Time().IsZero(), check.Equals, true) 65 // if not empty, returns the earliest one 66 c.Check(client.SnapshotSet{Snapshots: []*client.Snapshot{ 67 {Time: time.Unix(3, 0)}, 68 {Time: time.Unix(1, 0)}, 69 {Time: time.Unix(2, 0)}, 70 }}.Time(), check.DeepEquals, time.Unix(1, 0)) 71 } 72 73 func (cs *clientSuite) TestClientSnapshotSetSize(c *check.C) { 74 // if set is empty, doesn't explode (and returns 0) 75 c.Check(client.SnapshotSet{}.Size(), check.Equals, int64(0)) 76 // if not empty, returns the sum 77 c.Check(client.SnapshotSet{Snapshots: []*client.Snapshot{ 78 {Size: 1}, 79 {Size: 2}, 80 {Size: 3}, 81 }}.Size(), check.DeepEquals, int64(6)) 82 } 83 84 func (cs *clientSuite) TestClientSnapshotSets(c *check.C) { 85 cs.rsp = `{ 86 "type": "sync", 87 "result": [{"id": 1}, {"id":2}] 88 }` 89 sets, err := cs.cli.SnapshotSets(42, []string{"foo", "bar"}) 90 c.Assert(err, check.IsNil) 91 c.Check(sets, check.DeepEquals, []client.SnapshotSet{{ID: 1}, {ID: 2}}) 92 c.Check(cs.req.Method, check.Equals, "GET") 93 c.Check(cs.req.URL.Path, check.Equals, "/v2/snapshots") 94 c.Check(cs.req.URL.Query(), check.DeepEquals, url.Values{ 95 "set": []string{"42"}, 96 "snaps": []string{"foo,bar"}, 97 }) 98 } 99 100 func (cs *clientSuite) testClientSnapshotActionFull(c *check.C, action string, users []string, f func() (string, error)) { 101 cs.status = 202 102 cs.rsp = `{ 103 "status-code": 202, 104 "type": "async", 105 "change": "1too3" 106 }` 107 id, err := f() 108 c.Assert(err, check.IsNil) 109 c.Check(id, check.Equals, "1too3") 110 111 c.Assert(cs.req.Header.Get("Content-Type"), check.Equals, "application/json") 112 113 act, err := client.UnmarshalSnapshotAction(cs.req.Body) 114 c.Assert(err, check.IsNil) 115 c.Check(act.SetID, check.Equals, uint64(42)) 116 c.Check(act.Action, check.Equals, action) 117 c.Check(act.Snaps, check.DeepEquals, []string{"asnap", "bsnap"}) 118 c.Check(act.Users, check.DeepEquals, users) 119 120 c.Check(cs.req.Method, check.Equals, "POST") 121 c.Check(cs.req.URL.Path, check.Equals, "/v2/snapshots") 122 c.Check(cs.req.URL.Query(), check.HasLen, 0) 123 } 124 125 func (cs *clientSuite) TestClientForgetSnapshot(c *check.C) { 126 cs.testClientSnapshotActionFull(c, "forget", nil, func() (string, error) { 127 return cs.cli.ForgetSnapshots(42, []string{"asnap", "bsnap"}) 128 }) 129 } 130 131 func (cs *clientSuite) testClientSnapshotAction(c *check.C, action string, f func(uint64, []string, []string) (string, error)) { 132 cs.testClientSnapshotActionFull(c, action, []string{"auser", "buser"}, func() (string, error) { 133 return f(42, []string{"asnap", "bsnap"}, []string{"auser", "buser"}) 134 }) 135 } 136 137 func (cs *clientSuite) TestClientCheckSnapshots(c *check.C) { 138 cs.testClientSnapshotAction(c, "check", cs.cli.CheckSnapshots) 139 } 140 141 func (cs *clientSuite) TestClientRestoreSnapshots(c *check.C) { 142 cs.testClientSnapshotAction(c, "restore", cs.cli.RestoreSnapshots) 143 } 144 145 func (cs *clientSuite) TestClientExportSnapshot(c *check.C) { 146 type tableT struct { 147 content string 148 contentType string 149 status int 150 } 151 152 table := []tableT{ 153 {"dummy-export", client.SnapshotExportMediaType, 200}, 154 {"dummy-export", "application/x-tar", 400}, 155 {"", "", 400}, 156 } 157 158 for i, t := range table { 159 comm := check.Commentf("%d: %q", i, t.content) 160 161 cs.contentLength = int64(len(t.content)) 162 cs.header = http.Header{"Content-Type": []string{t.contentType}} 163 cs.rsp = t.content 164 cs.status = t.status 165 166 r, size, err := cs.cli.SnapshotExport(42) 167 if t.status == 200 { 168 c.Assert(err, check.IsNil, comm) 169 c.Assert(cs.countingCloser.closeCalled, check.Equals, 0) 170 c.Assert(size, check.Equals, int64(len(t.content)), comm) 171 } else { 172 c.Assert(err.Error(), check.Equals, "unexpected status code: ") 173 c.Assert(cs.countingCloser.closeCalled, check.Equals, 1) 174 } 175 176 if t.status == 200 { 177 buf, err := ioutil.ReadAll(r) 178 c.Assert(err, check.IsNil) 179 c.Assert(string(buf), check.Equals, t.content) 180 } 181 } 182 } 183 184 func (cs *clientSuite) TestClientSnapshotImport(c *check.C) { 185 type tableT struct { 186 rsp string 187 status int 188 setID uint64 189 error string 190 } 191 table := []tableT{ 192 {`{"type": "sync", "result": {"set-id": 42, "snaps": ["baz", "bar", "foo"]}}`, 200, 42, ""}, 193 {`{"type": "error"}`, 400, 0, "server error: \"Bad Request\""}, 194 } 195 196 for i, t := range table { 197 comm := check.Commentf("%d: %s", i, t.rsp) 198 199 cs.rsp = t.rsp 200 cs.status = t.status 201 202 fakeSnapshotData := "fake" 203 r := strings.NewReader(fakeSnapshotData) 204 importSet, err := cs.cli.SnapshotImport(r, int64(len(fakeSnapshotData))) 205 if t.error != "" { 206 c.Assert(err, check.NotNil, comm) 207 c.Check(err.Error(), check.Equals, t.error, comm) 208 continue 209 } 210 c.Assert(err, check.IsNil, comm) 211 c.Assert(cs.req.Header.Get("Content-Type"), check.Equals, client.SnapshotExportMediaType) 212 c.Assert(cs.req.Header.Get("Content-Length"), check.Equals, strconv.Itoa(len(fakeSnapshotData))) 213 c.Check(importSet.ID, check.Equals, t.setID, comm) 214 c.Check(importSet.Snaps, check.DeepEquals, []string{"baz", "bar", "foo"}, comm) 215 d, err := ioutil.ReadAll(cs.req.Body) 216 c.Assert(err, check.IsNil) 217 c.Check(string(d), check.Equals, fakeSnapshotData) 218 } 219 }