github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/cmd/snap/cmd_validate_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2020 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 main_test
    21  
    22  import (
    23  	"fmt"
    24  	"io/ioutil"
    25  	"net/http"
    26  
    27  	"gopkg.in/check.v1"
    28  
    29  	"github.com/snapcore/snapd/cmd/snap"
    30  )
    31  
    32  type validateSuite struct {
    33  	BaseSnapSuite
    34  }
    35  
    36  var _ = check.Suite(&validateSuite{})
    37  
    38  func makeFakeValidationSetPostHandler(c *check.C, body, action string, sequence int) func(w http.ResponseWriter, r *http.Request) {
    39  	var called bool
    40  	return func(w http.ResponseWriter, r *http.Request) {
    41  		if called {
    42  			c.Fatalf("expected a single request")
    43  		}
    44  		called = true
    45  		c.Check(r.URL.Path, check.Equals, "/v2/validation-sets/foo/bar")
    46  		c.Check(r.Method, check.Equals, "POST")
    47  
    48  		buf, err := ioutil.ReadAll(r.Body)
    49  		c.Assert(err, check.IsNil)
    50  		switch {
    51  		case sequence != 0 && action != "forget":
    52  			c.Check(string(buf), check.DeepEquals, fmt.Sprintf("{\"action\":\"apply\",\"mode\":%q,\"sequence\":%d}\n", action, sequence))
    53  		case sequence == 0 && action != "forget":
    54  			c.Check(string(buf), check.DeepEquals, fmt.Sprintf("{\"action\":\"apply\",\"mode\":%q}\n", action))
    55  		case sequence != 0 && action == "forget":
    56  			c.Check(string(buf), check.DeepEquals, fmt.Sprintf("{\"action\":\"forget\",\"sequence\":%d}\n", sequence))
    57  		case action == "forget":
    58  			c.Check(string(buf), check.DeepEquals, "{\"action\":\"forget\"}\n")
    59  		default:
    60  			c.Fatalf("unexpected action: %s", action)
    61  		}
    62  
    63  		w.WriteHeader(200)
    64  		fmt.Fprintln(w, body)
    65  	}
    66  }
    67  
    68  func makeFakeValidationSetQueryHandler(c *check.C, body string) func(w http.ResponseWriter, r *http.Request) {
    69  	var called bool
    70  	return func(w http.ResponseWriter, r *http.Request) {
    71  		if called {
    72  			c.Fatalf("expected a single request")
    73  		}
    74  		called = true
    75  		c.Check(r.URL.Path, check.Equals, "/v2/validation-sets/foo/bar")
    76  		c.Check(r.Method, check.Equals, "GET")
    77  		w.WriteHeader(200)
    78  		fmt.Fprintln(w, body)
    79  	}
    80  }
    81  
    82  func makeFakeListValidationsSetsHandler(c *check.C, body string) func(w http.ResponseWriter, r *http.Request) {
    83  	var called bool
    84  	return func(w http.ResponseWriter, r *http.Request) {
    85  		if called {
    86  			c.Fatalf("expected a single request")
    87  		}
    88  		called = true
    89  		c.Check(r.URL.Path, check.Equals, "/v2/validation-sets")
    90  		c.Check(r.Method, check.Equals, "GET")
    91  		w.WriteHeader(200)
    92  		fmt.Fprintln(w, body)
    93  	}
    94  }
    95  
    96  func (s *validateSuite) TestValidateInvalidArgs(c *check.C) {
    97  	for _, args := range []struct {
    98  		args []string
    99  		err  string
   100  	}{
   101  		{[]string{"foo"}, `cannot parse validation set "foo": expected a single account/name`},
   102  		{[]string{"foo/bar/baz"}, `cannot parse validation set "foo/bar/baz": expected a single account/name`},
   103  		{[]string{"--monitor", "--enforce"}, `cannot use --monitor and --enforce together`},
   104  		{[]string{"--monitor", "--forget"}, `cannot use --monitor and --forget together`},
   105  		{[]string{"--enforce", "--forget"}, `cannot use --enforce and --forget together`},
   106  		{[]string{"--enforce"}, `missing validation set argument`},
   107  		{[]string{"--monitor"}, `missing validation set argument`},
   108  		{[]string{"--forget"}, `missing validation set argument`},
   109  		{[]string{"--forget", "foo/-"}, `cannot parse validation set "foo/-": invalid validation set name "-"`},
   110  	} {
   111  		s.stdout.Reset()
   112  		s.stderr.Reset()
   113  
   114  		_, err := main.Parser(main.Client()).ParseArgs(append([]string{"validate"}, args.args...))
   115  		c.Assert(err, check.ErrorMatches, args.err)
   116  	}
   117  }
   118  
   119  func (s *validateSuite) TestValidateMonitor(c *check.C) {
   120  	s.RedirectClientToTestServer(makeFakeValidationSetPostHandler(c, `{"type": "sync", "status-code": 200, "result": []}`, "monitor", 0))
   121  
   122  	rest, err := main.Parser(main.Client()).ParseArgs([]string{"validate", "--monitor", "foo/bar"})
   123  	c.Assert(err, check.IsNil)
   124  	c.Check(rest, check.HasLen, 0)
   125  	c.Check(s.Stderr(), check.Equals, "")
   126  	c.Check(s.Stdout(), check.Equals, "")
   127  }
   128  
   129  func (s *validateSuite) TestValidateMonitorPinned(c *check.C) {
   130  	s.RedirectClientToTestServer(makeFakeValidationSetPostHandler(c, `{"type": "sync", "status-code": 200, "result": []}`, "monitor", 3))
   131  
   132  	rest, err := main.Parser(main.Client()).ParseArgs([]string{"validate", "--monitor", "foo/bar=3"})
   133  	c.Assert(err, check.IsNil)
   134  	c.Check(rest, check.HasLen, 0)
   135  	c.Check(s.Stderr(), check.Equals, "")
   136  	c.Check(s.Stdout(), check.Equals, "")
   137  }
   138  
   139  func (s *validateSuite) TestValidateEnforce(c *check.C) {
   140  	s.RedirectClientToTestServer(makeFakeValidationSetPostHandler(c, `{"type": "sync", "status-code": 200, "result": []}`, "enforce", 0))
   141  
   142  	rest, err := main.Parser(main.Client()).ParseArgs([]string{"validate", "--enforce", "foo/bar"})
   143  	c.Assert(err, check.IsNil)
   144  	c.Check(rest, check.HasLen, 0)
   145  	c.Check(s.Stderr(), check.Equals, "")
   146  	c.Check(s.Stdout(), check.Equals, "")
   147  }
   148  
   149  func (s *validateSuite) TestValidateEnforcePinned(c *check.C) {
   150  	s.RedirectClientToTestServer(makeFakeValidationSetPostHandler(c, `{"type": "sync", "status-code": 200, "result": []}`, "enforce", 5))
   151  
   152  	rest, err := main.Parser(main.Client()).ParseArgs([]string{"validate", "--enforce", "foo/bar=5"})
   153  	c.Assert(err, check.IsNil)
   154  	c.Check(rest, check.HasLen, 0)
   155  	c.Check(s.Stderr(), check.Equals, "")
   156  	c.Check(s.Stdout(), check.Equals, "")
   157  }
   158  
   159  func (s *validateSuite) TestValidateForget(c *check.C) {
   160  	s.RedirectClientToTestServer(makeFakeValidationSetPostHandler(c, `{"type": "sync", "status-code": 200, "result": []}`, "forget", 0))
   161  
   162  	rest, err := main.Parser(main.Client()).ParseArgs([]string{"validate", "--forget", "foo/bar"})
   163  	c.Assert(err, check.IsNil)
   164  	c.Check(rest, check.HasLen, 0)
   165  	c.Check(s.Stderr(), check.Equals, "")
   166  	c.Check(s.Stdout(), check.Equals, "")
   167  }
   168  
   169  func (s *validateSuite) TestValidateForgetPinned(c *check.C) {
   170  	s.RedirectClientToTestServer(makeFakeValidationSetPostHandler(c, `{"type": "sync", "status-code": 200, "result": []}`, "forget", 5))
   171  
   172  	rest, err := main.Parser(main.Client()).ParseArgs([]string{"validate", "--forget", "foo/bar=5"})
   173  	c.Assert(err, check.IsNil)
   174  	c.Check(rest, check.HasLen, 0)
   175  	c.Check(s.Stderr(), check.Equals, "")
   176  	c.Check(s.Stdout(), check.Equals, "")
   177  }
   178  
   179  func (s *validateSuite) TestValidateQueryOne(c *check.C) {
   180  	restore := main.MockIsStdinTTY(true)
   181  	defer restore()
   182  
   183  	s.RedirectClientToTestServer(makeFakeValidationSetQueryHandler(c, `{"type": "sync", "status-code": 200, "result": {"account-id":"foo","name":"bar","mode":"monitor","sequence":3,"valid":true}}`))
   184  
   185  	rest, err := main.Parser(main.Client()).ParseArgs([]string{"validate", "foo/bar"})
   186  	c.Assert(err, check.IsNil)
   187  	c.Check(rest, check.HasLen, 0)
   188  	c.Check(s.Stderr(), check.Equals, "")
   189  	c.Check(s.Stdout(), check.Equals, "valid")
   190  }
   191  
   192  func (s *validateSuite) TestValidateQueryOneInvalid(c *check.C) {
   193  	restore := main.MockIsStdinTTY(true)
   194  	defer restore()
   195  
   196  	s.RedirectClientToTestServer(makeFakeValidationSetQueryHandler(c, `{"type": "sync", "status-code": 200, "result": {"account-id":"foo","name":"bar","mode":"monitor","sequence":3,"valid":false}}`))
   197  
   198  	rest, err := main.Parser(main.Client()).ParseArgs([]string{"validate", "foo/bar"})
   199  	c.Assert(err, check.IsNil)
   200  	c.Check(rest, check.HasLen, 0)
   201  	c.Check(s.Stderr(), check.Equals, "")
   202  	c.Check(s.Stdout(), check.Equals, "invalid")
   203  }
   204  
   205  func (s *validateSuite) TestValidationSetsList(c *check.C) {
   206  	restore := main.MockIsStdinTTY(true)
   207  	defer restore()
   208  
   209  	s.RedirectClientToTestServer(makeFakeListValidationsSetsHandler(c, `{"type": "sync", "status-code": 200, "result": [
   210  		{"account-id":"foo","name":"bar","mode":"monitor","pinned-at":2,"sequence":3,"valid":true},
   211  		{"account-id":"foo","name":"baz","mode":"enforce","sequence":1,"valid":false}
   212  	]}`))
   213  
   214  	rest, err := main.Parser(main.Client()).ParseArgs([]string{"validate"})
   215  	c.Assert(err, check.IsNil)
   216  	c.Check(rest, check.HasLen, 0)
   217  	c.Check(s.Stderr(), check.Equals, "")
   218  	c.Check(s.Stdout(), check.Equals, "Validation  Mode     Seq  Current       Notes\n"+
   219  		"foo/bar=2   monitor  3    valid    \n"+
   220  		"foo/baz     enforce  1    invalid  \n",
   221  	)
   222  }
   223  
   224  func (s *validateSuite) TestValidationSetsListEmpty(c *check.C) {
   225  	restore := main.MockIsStdinTTY(true)
   226  	defer restore()
   227  
   228  	s.RedirectClientToTestServer(makeFakeListValidationsSetsHandler(c, `{"type": "sync", "status-code": 200, "result": []}`))
   229  
   230  	rest, err := main.Parser(main.Client()).ParseArgs([]string{"validate"})
   231  	c.Assert(err, check.IsNil)
   232  	c.Check(rest, check.HasLen, 0)
   233  	c.Check(s.Stderr(), check.Equals, "No validations are available\n")
   234  	c.Check(s.Stdout(), check.Equals, "")
   235  }