github.com/sqlitebrowser/dio@v0.0.0-20240125125356-b587368e5c6b/cmd/dio_test.go (about)

     1  package cmd
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"crypto/sha256"
     7  	"crypto/tls"
     8  	"crypto/x509"
     9  	"encoding/hex"
    10  	"flag"
    11  	"fmt"
    12  	"io"
    13  	"log"
    14  	"net/http"
    15  	"os"
    16  	"path/filepath"
    17  	"strings"
    18  	"testing"
    19  	"time"
    20  
    21  	"github.com/spf13/viper"
    22  	chk "gopkg.in/check.v1"
    23  )
    24  
    25  type DioSuite struct {
    26  	buf    bytes.Buffer
    27  	config string
    28  	dbFile string
    29  	dbName string
    30  	oldOut io.Writer
    31  }
    32  
    33  const (
    34  	CONFIG = `[certs]
    35  cachain = "%s"
    36  cert = "%s"
    37  
    38  [general]
    39  cloud = "%s"
    40  
    41  [user]
    42  name = "Some One"
    43  `
    44  )
    45  
    46  var (
    47  	_            = chk.Suite(&DioSuite{})
    48  	licFile      string
    49  	remoteServer = flag.String("remote", "https://localhost:5550", "URL of remote server to test against")
    50  	showFlag     = flag.Bool("show", false, "Don't redirect test command output to /dev/null")
    51  	tempDir      string
    52  )
    53  
    54  func Test(t *testing.T) {
    55  	chk.TestingT(t)
    56  }
    57  
    58  func (s *DioSuite) SetUpSuite(c *chk.C) {
    59  	// Create initial config file in a temp directory
    60  	tempDir = c.MkDir()
    61  	fmt.Printf("Temp dir: %s\n", tempDir)
    62  	s.config = filepath.Join(tempDir, "config.toml")
    63  	f, err := os.Create(s.config)
    64  	if err != nil {
    65  		log.Fatalln(err)
    66  	}
    67  	defer f.Close()
    68  	d, err := os.Getwd()
    69  	if err != nil {
    70  		log.Fatalln(err)
    71  	}
    72  	_, err = fmt.Fprintf(f, CONFIG,
    73  		filepath.Join(d, "..", "test_data", "ca-chain-docker.cert.pem"),
    74  		filepath.Join(tempDir, "default.cert.pem"),
    75  		*remoteServer)
    76  	if err != nil {
    77  		log.Fatalln(err)
    78  	}
    79  
    80  	// Generate new client certificate for testing purposes
    81  	err = genTestCert(*remoteServer, filepath.Join(tempDir, "default.cert.pem"))
    82  	if err != nil {
    83  		log.Fatal(err)
    84  	}
    85  
    86  	// Seed the database
    87  	err = seedTests(*remoteServer)
    88  	if err != nil {
    89  		log.Fatal(err)
    90  	}
    91  
    92  	// Drop any old config loaded automatically by viper, and use our temporary test config instead
    93  	viper.Reset()
    94  	viper.SetConfigFile(s.config)
    95  	if err = viper.ReadInConfig(); err != nil {
    96  		log.Fatalf("Error loading test config file: %s", err)
    97  		return
    98  	}
    99  	cloud = viper.GetString("general.cloud")
   100  
   101  	// Use our testing certificates
   102  	ourCAPool := x509.NewCertPool()
   103  	chainFile, err := os.ReadFile(filepath.Join(d, "..", "test_data", "ca-chain-docker.cert.pem"))
   104  	if err != nil {
   105  		log.Fatalln(err)
   106  	}
   107  	ok := ourCAPool.AppendCertsFromPEM(chainFile)
   108  	if !ok {
   109  		log.Fatalln("Error when loading certificate chain file")
   110  	}
   111  	testCert := filepath.Join(d, "..", "test_data", "default.cert.pem")
   112  	cert, err := tls.LoadX509KeyPair(testCert, testCert)
   113  	if err != nil {
   114  		log.Fatalln(err)
   115  	}
   116  	TLSConfig.Certificates = []tls.Certificate{cert}
   117  
   118  	// Load our self signed CA Cert chain, and set TLS1.2 as minimum
   119  	TLSConfig = tls.Config{
   120  		Certificates:             []tls.Certificate{cert},
   121  		ClientCAs:                ourCAPool,
   122  		InsecureSkipVerify:       true,
   123  		MinVersion:               tls.VersionTLS12,
   124  		PreferServerCipherSuites: true,
   125  		RootCAs:                  ourCAPool,
   126  	}
   127  
   128  	var email string
   129  	certUser, email, _, err = getUserAndServer()
   130  	if err != nil {
   131  		log.Fatalln(err)
   132  	}
   133  	viper.Set("user.email", email)
   134  
   135  	// Add test database
   136  	s.dbName = "19kB.sqlite"
   137  	db, err := os.ReadFile(filepath.Join(d, "..", "test_data", s.dbName))
   138  	if err != nil {
   139  		log.Fatalln(err)
   140  	}
   141  	s.dbFile = filepath.Join(tempDir, s.dbName)
   142  	err = os.WriteFile(s.dbFile, db, 0644)
   143  	if err != nil {
   144  		log.Fatalln(err)
   145  	}
   146  
   147  	// Set the last modified date of the database file to a known value
   148  	err = os.Chtimes(s.dbFile, time.Now(), time.Date(2019, time.March, 15, 18, 1, 0, 0, time.UTC))
   149  	if err != nil {
   150  		log.Fatalln(err)
   151  	}
   152  
   153  	// Add a test licence
   154  	lic, err := os.ReadFile(filepath.Join(d, "..", "LICENSE"))
   155  	if err != nil {
   156  		log.Fatalln(err)
   157  	}
   158  	licFile = filepath.Join(tempDir, "test.licence")
   159  	err = os.WriteFile(licFile, lic, 0644)
   160  	if err != nil {
   161  		log.Fatalln(err)
   162  	}
   163  
   164  	// If not told otherwise, redirect command output to /dev/null
   165  	if !*showFlag {
   166  		fOut, err = os.OpenFile(os.DevNull, os.O_APPEND|os.O_WRONLY, 0644)
   167  		if err != nil {
   168  			log.Fatalln(err)
   169  		}
   170  	}
   171  
   172  	// Change to the temp directory
   173  	err = os.Chdir(tempDir)
   174  	c.Assert(err, chk.IsNil)
   175  }
   176  
   177  func (s *DioSuite) SetUpTest(c *chk.C) {
   178  	// TODO: Should we use io.TeeReader if showFlag has been set?
   179  	// Redirect display output to a temp buffer
   180  	s.oldOut = fOut
   181  	fOut = &s.buf
   182  }
   183  
   184  func (s *DioSuite) TearDownTest(c *chk.C) {
   185  	// Restore the display output redirection
   186  	fOut = s.oldOut
   187  
   188  	// Clear the buffered contents
   189  	s.buf.Reset()
   190  }
   191  
   192  // Test the "dio commit" command
   193  func (s *DioSuite) Test0010_Commit(c *chk.C) {
   194  	// Call the commit code
   195  	commitCmdBranch = "main"
   196  	commitCmdCommit = ""
   197  	commitCmdAuthEmail = "testdefault@dbhub.io"
   198  	commitCmdLicence = "Not specified"
   199  	commitCmdMsg = "The first commit in our test run"
   200  	commitCmdAuthName = "Default test user"
   201  	commitCmdTimestamp = time.Date(2019, time.March, 15, 18, 1, 1, 0, time.UTC).Format(time.RFC3339)
   202  	// TODO: Adjust commit() to return the commit ID, so we don't need to hard code it below
   203  	err := commit([]string{s.dbName})
   204  	c.Assert(err, chk.IsNil)
   205  
   206  	// * Verify the new commit data on disk matches our expectations *
   207  
   208  	// Check if the metadata file exists on disk
   209  	meta, err := localFetchMetadata(s.dbName, false)
   210  	c.Assert(err, chk.IsNil)
   211  	c.Check(meta.Commits, chk.HasLen, 1)
   212  
   213  	// Verify the values in the commit data match the values we provided
   214  	com, ok := meta.Commits["59b72b78cb83bdba371438cb36950fe007265445a63068ae5586c9cc19203941"] // This commit ID is what the given values should generate a commit ID as
   215  	c.Assert(ok, chk.Equals, true)
   216  	c.Check(com.AuthorName, chk.Equals, commitCmdAuthName)
   217  	c.Check(com.AuthorEmail, chk.Equals, commitCmdAuthEmail)
   218  	c.Check(com.Message, chk.Equals, commitCmdMsg)
   219  	c.Check(com.Timestamp, chk.Equals, time.Date(2019, time.March, 15, 18, 1, 1, 0, time.UTC))
   220  	c.Check(com.Parent, chk.Equals, "")
   221  	c.Check(com.OtherParents, chk.IsNil)
   222  	c.Check(com.CommitterName, chk.Equals, "Some One")
   223  	c.Check(com.CommitterEmail, chk.Equals, "default@docker-dev.dbhub.io")
   224  	c.Check(com.ID, chk.Equals, "59b72b78cb83bdba371438cb36950fe007265445a63068ae5586c9cc19203941")
   225  	c.Check(com.Tree.Entries[0].EntryType, chk.Equals, dbTreeEntryType(DATABASE))
   226  	c.Check(com.Tree.Entries[0].LastModified.UTC(), chk.Equals, time.Date(2019, time.March, 15, 18, 1, 0, 0, time.UTC))
   227  	c.Check(com.Tree.Entries[0].LicenceSHA, chk.Equals, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855") // e3b... is the SHA256 for the "Not specified" licence option
   228  	c.Check(int(com.Tree.Entries[0].Size), chk.Equals, 19456)
   229  	c.Check(com.Tree.Entries[0].Name, chk.Equals, s.dbName)
   230  
   231  	// Check the database has been written to the cache area using its checksum as filename
   232  	cacheFile := filepath.Join(".dio", s.dbName, "db", com.Tree.Entries[0].Sha256)
   233  	_, err = os.Stat(cacheFile)
   234  	c.Assert(err, chk.IsNil)
   235  
   236  	// Verify the contents of the cached database match the size and sha256 recorded in the commit
   237  	b, err := os.ReadFile(cacheFile)
   238  	c.Assert(err, chk.IsNil)
   239  	c.Check(b, chk.HasLen, int(com.Tree.Entries[0].Size))
   240  	z := sha256.Sum256(b)
   241  	shaSum := hex.EncodeToString(z[:])
   242  	c.Check(shaSum, chk.Equals, com.Tree.Entries[0].Sha256)
   243  
   244  	// Verify the branch info
   245  	br, ok := meta.Branches["main"]
   246  	c.Assert(ok, chk.Equals, true)
   247  	c.Check(br.Commit, chk.Equals, "59b72b78cb83bdba371438cb36950fe007265445a63068ae5586c9cc19203941")
   248  	c.Check(br.CommitCount, chk.Equals, 1)
   249  	c.Check(br.Description, chk.Equals, "")
   250  }
   251  
   252  func (s *DioSuite) Test0020_Commit2(c *chk.C) {
   253  	// Change the last modified date on the database file
   254  	err := os.Chtimes(s.dbFile, time.Now(), time.Date(2019, time.March, 15, 18, 1, 2, 0, time.UTC))
   255  	c.Assert(err, chk.IsNil)
   256  
   257  	// Create another commit
   258  	commitCmdMsg = "The second commit in our test run"
   259  	commitCmdTimestamp = "2019-03-15T18:01:03Z"
   260  	err = commit([]string{s.dbName})
   261  	c.Assert(err, chk.IsNil)
   262  
   263  	// * Verify the new commit data on disk matches our expectations *
   264  
   265  	// Check if the metadata file exists on disk
   266  	var meta metaData
   267  	meta, err = localFetchMetadata(s.dbName, false)
   268  	c.Assert(err, chk.IsNil)
   269  	c.Check(meta.Commits, chk.HasLen, 2)
   270  
   271  	// Verify the values in the commit data match the values we provided
   272  	com, ok := meta.Commits["70815d687cfc614b23dfb2f66f8fa0a8cb7bad199e05d4142db883690eefeba5"] // This commit ID is what the given values should generate a commit ID as
   273  	c.Assert(ok, chk.Equals, true)
   274  	c.Check(com.AuthorName, chk.Equals, commitCmdAuthName)
   275  	c.Check(com.AuthorEmail, chk.Equals, commitCmdAuthEmail)
   276  	c.Check(com.Message, chk.Equals, commitCmdMsg)
   277  	c.Check(com.Timestamp, chk.Equals, time.Date(2019, time.March, 15, 18, 1, 3, 0, time.UTC))
   278  	c.Check(com.Parent, chk.Equals, "59b72b78cb83bdba371438cb36950fe007265445a63068ae5586c9cc19203941")
   279  	c.Check(com.OtherParents, chk.IsNil)
   280  	c.Check(com.CommitterName, chk.Equals, "Some One")
   281  	c.Check(com.CommitterEmail, chk.Equals, "default@docker-dev.dbhub.io")
   282  	c.Check(com.ID, chk.Equals, "70815d687cfc614b23dfb2f66f8fa0a8cb7bad199e05d4142db883690eefeba5")
   283  	c.Check(com.Tree.Entries[0].EntryType, chk.Equals, dbTreeEntryType(DATABASE))
   284  	c.Check(com.Tree.Entries[0].LastModified.UTC(), chk.Equals, time.Date(2019, time.March, 15, 18, 1, 2, 0, time.UTC))
   285  	c.Check(com.Tree.Entries[0].LicenceSHA, chk.Equals, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855") // e3b... is the SHA256 for the "Not specified" licence option
   286  	c.Check(int(com.Tree.Entries[0].Size), chk.Equals, 19456)
   287  	c.Check(com.Tree.Entries[0].Name, chk.Equals, s.dbName)
   288  
   289  	// Check the database has been written to the cache area using its checksum as filename
   290  	cacheFile := filepath.Join(".dio", s.dbName, "db", com.Tree.Entries[0].Sha256)
   291  	_, err = os.Stat(cacheFile)
   292  	c.Assert(err, chk.IsNil)
   293  
   294  	// Verify the contents of the cached database match the size and sha256 recorded in the commit
   295  	b, err := os.ReadFile(cacheFile)
   296  	c.Assert(err, chk.IsNil)
   297  	c.Check(b, chk.HasLen, int(com.Tree.Entries[0].Size))
   298  	z := sha256.Sum256(b)
   299  	shaSum := hex.EncodeToString(z[:])
   300  	c.Check(shaSum, chk.Equals, com.Tree.Entries[0].Sha256)
   301  
   302  	// Verify the branch info
   303  	br, ok := meta.Branches["main"]
   304  	c.Assert(ok, chk.Equals, true)
   305  	c.Check(br.Commit, chk.Equals, "70815d687cfc614b23dfb2f66f8fa0a8cb7bad199e05d4142db883690eefeba5")
   306  	c.Check(br.CommitCount, chk.Equals, 2)
   307  	c.Check(br.Description, chk.Equals, "")
   308  
   309  	// TODO: Now that we can capture the display output for checking, we should probably verify the
   310  	//       info displayed in the output here too
   311  }
   312  
   313  // Test the "dio branch" commands
   314  func (s *DioSuite) Test0030_BranchActiveGet(c *chk.C) {
   315  	// Query the active branch
   316  	err := branchActiveGet([]string{s.dbName})
   317  	c.Assert(err, chk.IsNil)
   318  
   319  	// Verify the active branch is set to "main"
   320  	p := strings.Split(s.buf.String(), ":")
   321  	c.Assert(p, chk.HasLen, 2)
   322  	c.Check(strings.TrimSpace(p[1]), chk.Equals, "main")
   323  }
   324  
   325  func (s *DioSuite) Test0040_BranchCreate(c *chk.C) {
   326  	// Create a new branch
   327  	branchCreateBranch = "branchtwo"
   328  	branchCreateCommit = "59b72b78cb83bdba371438cb36950fe007265445a63068ae5586c9cc19203941"
   329  	branchCreateMsg = "A new branch"
   330  	err := branchCreate([]string{s.dbName})
   331  	c.Assert(err, chk.IsNil)
   332  
   333  	// Verify the new branch is in the metadata on disk
   334  	meta, err := localFetchMetadata(s.dbName, false)
   335  	c.Assert(err, chk.IsNil)
   336  	br, ok := meta.Branches["branchtwo"]
   337  	c.Assert(ok, chk.Equals, true)
   338  	c.Check(br.Commit, chk.Equals, "59b72b78cb83bdba371438cb36950fe007265445a63068ae5586c9cc19203941")
   339  	c.Check(br.CommitCount, chk.Equals, 1)
   340  	c.Check(br.Description, chk.Equals, "A new branch")
   341  
   342  	// Verify the output given to the user
   343  	p := strings.Split(s.buf.String(), "'")
   344  	c.Assert(p, chk.HasLen, 3)
   345  	c.Check(strings.TrimSpace(p[1]), chk.Equals, branchCreateBranch)
   346  }
   347  
   348  func (s *DioSuite) Test0050_BranchSetBranchTwo(c *chk.C) {
   349  	// Switch to the new branch
   350  	branchActiveSetBranch = "branchtwo"
   351  	err := branchActiveSet([]string{s.dbName})
   352  	c.Assert(err, chk.IsNil)
   353  
   354  	// Verify the active branch was changed in the on disk metadata
   355  	meta, err := localFetchMetadata(s.dbName, false)
   356  	c.Assert(err, chk.IsNil)
   357  	c.Check(meta.ActiveBranch, chk.Equals, branchActiveSetBranch)
   358  
   359  	// Verify the output given to the user
   360  	p := strings.Split(s.buf.String(), "'")
   361  	c.Check(strings.TrimSpace(p[1]), chk.Equals, branchActiveSetBranch)
   362  }
   363  
   364  func (s *DioSuite) Test0060_BranchList(c *chk.C) {
   365  	// Create a new branch
   366  	err := branchList([]string{s.dbName})
   367  	c.Assert(err, chk.IsNil)
   368  
   369  	// Verify entries are present for both "main" and "branchtwo"
   370  	lines := bufio.NewScanner(&s.buf)
   371  	var branchTwoFound, mainFound bool
   372  	for lines.Scan() {
   373  		p := strings.Split(lines.Text(), "'")
   374  		if len(p) > 2 && p[1] == "main" {
   375  			c.Check(p, chk.HasLen, 3)
   376  			mainFound = true
   377  		}
   378  		if len(p) > 2 && p[1] == "branchtwo" {
   379  			c.Check(p, chk.HasLen, 3)
   380  			branchTwoFound = true
   381  		}
   382  	}
   383  	c.Check(mainFound, chk.Equals, true)
   384  	c.Check(branchTwoFound, chk.Equals, true)
   385  }
   386  
   387  func (s *DioSuite) Test0070_BranchRemoveFail(c *chk.C) {
   388  	// Attempt to remove the branch (should fail)
   389  	branchRemoveBranch = "branchtwo"
   390  	err := branchRemove([]string{s.dbName})
   391  	c.Assert(err, chk.Not(chk.IsNil))
   392  
   393  	// Make sure both the "main" and "branchtwo" branches are still present on disk
   394  	meta, err := localFetchMetadata(s.dbName, false)
   395  	c.Assert(err, chk.IsNil)
   396  	_, ok := meta.Branches["main"]
   397  	c.Assert(ok, chk.Equals, true)
   398  	_, ok = meta.Branches["branchtwo"]
   399  	c.Assert(ok, chk.Equals, true)
   400  
   401  	// TODO: When the display of error messages to the user is a bit better finalised,
   402  	//       add a check of the output here
   403  }
   404  
   405  func (s *DioSuite) Test0080_BranchSetMain(c *chk.C) {
   406  	// Switch to the main branch
   407  	branchActiveSetBranch = "main"
   408  	err := branchActiveSet([]string{s.dbName})
   409  	c.Assert(err, chk.IsNil)
   410  
   411  	// Verify the active branch was changed in the on disk metadata
   412  	meta, err := localFetchMetadata(s.dbName, false)
   413  	c.Assert(err, chk.IsNil)
   414  	c.Check(meta.ActiveBranch, chk.Equals, branchActiveSetBranch)
   415  
   416  	// Verify the output given to the user
   417  	p := strings.Split(s.buf.String(), "'")
   418  	c.Check(strings.TrimSpace(p[1]), chk.Equals, branchActiveSetBranch)
   419  }
   420  
   421  func (s *DioSuite) Test0090_BranchRemoveSuccess(c *chk.C) {
   422  	// Attempt to remove the branch (should succeed)
   423  	branchRemoveBranch = "branchtwo"
   424  	err := branchRemove([]string{s.dbName})
   425  	c.Assert(err, chk.IsNil)
   426  
   427  	// Make sure the "main" branch is still present on disk, but "branchtwo" isn't
   428  	meta, err := localFetchMetadata(s.dbName, false)
   429  	c.Assert(err, chk.IsNil)
   430  	_, ok := meta.Branches["main"]
   431  	c.Assert(ok, chk.Equals, true)
   432  	_, ok = meta.Branches["branchtwo"]
   433  	c.Assert(ok, chk.Equals, false)
   434  
   435  	// Verify the output given to the user
   436  	p := strings.Split(s.buf.String(), "'")
   437  	c.Check(strings.TrimSpace(p[1]), chk.Equals, branchRemoveBranch)
   438  }
   439  
   440  func (s *DioSuite) Test0100_BranchRevert(c *chk.C) {
   441  	// Verify that (prior to the revert) the main branch still points to the 2nd commit
   442  	meta, err := localFetchMetadata(s.dbName, false)
   443  	c.Assert(err, chk.IsNil)
   444  	br, ok := meta.Branches["main"]
   445  	c.Assert(ok, chk.Equals, true)
   446  	c.Check(br.Commit, chk.Equals, "70815d687cfc614b23dfb2f66f8fa0a8cb7bad199e05d4142db883690eefeba5")
   447  	c.Check(br.CommitCount, chk.Equals, 2)
   448  	c.Check(br.Description, chk.Equals, "")
   449  
   450  	// Revert the main branch back to the original commit
   451  	branchRevertBranch = "main"
   452  	branchRevertCommit = "59b72b78cb83bdba371438cb36950fe007265445a63068ae5586c9cc19203941"
   453  	err = branchRevert([]string{s.dbName})
   454  	c.Assert(err, chk.IsNil)
   455  
   456  	// Verify the main branch now points to the original commit
   457  	meta, err = localFetchMetadata(s.dbName, false)
   458  	c.Assert(err, chk.IsNil)
   459  	br, ok = meta.Branches["main"]
   460  	c.Assert(ok, chk.Equals, true)
   461  	c.Check(br.Commit, chk.Equals, "59b72b78cb83bdba371438cb36950fe007265445a63068ae5586c9cc19203941")
   462  	c.Check(br.CommitCount, chk.Equals, 1)
   463  	c.Check(br.Description, chk.Equals, "")
   464  
   465  	// Verify the output given to the user
   466  	c.Check(strings.TrimSpace(s.buf.String()), chk.Equals, "Branch reverted")
   467  }
   468  
   469  func (s *DioSuite) Test0110_BranchUpdateChgDesc(c *chk.C) {
   470  	// Verify that (prior to the update) the main branch has an empty description
   471  	meta, err := localFetchMetadata(s.dbName, false)
   472  	c.Assert(err, chk.IsNil)
   473  	br, ok := meta.Branches["main"]
   474  	c.Assert(ok, chk.Equals, true)
   475  	c.Check(br.Description, chk.Equals, "")
   476  
   477  	// Update description for the main branch
   478  	branchUpdateBranch = "main"
   479  	branchUpdateMsg = "This is a new description"
   480  	err = branchUpdate([]string{s.dbName})
   481  	c.Assert(err, chk.IsNil)
   482  
   483  	// Verify the description was correctly updated
   484  	meta, err = localFetchMetadata(s.dbName, false)
   485  	c.Assert(err, chk.IsNil)
   486  	br, ok = meta.Branches["main"]
   487  	c.Assert(ok, chk.Equals, true)
   488  	c.Check(br.Description, chk.Equals, branchUpdateMsg)
   489  }
   490  
   491  func (s *DioSuite) Test0120_BranchUpdateDelDesc(c *chk.C) {
   492  	// Verify that (prior to the update) the main branch has a non-empty description
   493  	meta, err := localFetchMetadata(s.dbName, false)
   494  	c.Assert(err, chk.IsNil)
   495  	br, ok := meta.Branches["main"]
   496  	c.Assert(ok, chk.Equals, true)
   497  	c.Check(br.Description, chk.Not(chk.Equals), "")
   498  
   499  	// Delete the description for the main branch
   500  	*descDel = true
   501  	err = branchUpdate([]string{s.dbName})
   502  	c.Assert(err, chk.IsNil)
   503  
   504  	// Verify the description was deleted
   505  	meta, err = localFetchMetadata(s.dbName, false)
   506  	c.Assert(err, chk.IsNil)
   507  	br, ok = meta.Branches["main"]
   508  	c.Assert(ok, chk.Equals, true)
   509  	c.Check(br.Description, chk.Equals, "")
   510  
   511  	// Verify the output given to the user
   512  	c.Check(strings.TrimSpace(s.buf.String()), chk.Equals, "Branch updated")
   513  }
   514  
   515  func (s *DioSuite) Test0130_TagCreate(c *chk.C) {
   516  	// Check the tag to be created doesn't yet exist
   517  	tagCreateTag = "testtag1"
   518  	meta, err := localFetchMetadata(s.dbName, false)
   519  	c.Assert(err, chk.IsNil)
   520  	z, ok := meta.Tags[tagCreateTag]
   521  	c.Assert(ok, chk.Equals, false)
   522  
   523  	// Create the tag
   524  	tagCreateCommit = "59b72b78cb83bdba371438cb36950fe007265445a63068ae5586c9cc19203941"
   525  	tagCreateDate = "2019-03-15T18:01:05Z"
   526  	tagCreateEmail = "sometagger@example.org"
   527  	tagCreateMsg = "This is a test tag"
   528  	tagCreateName = "A test tagger"
   529  	err = tagCreate([]string{s.dbName})
   530  	c.Assert(err, chk.IsNil)
   531  
   532  	// Check the tag was created
   533  	meta, err = localFetchMetadata(s.dbName, false)
   534  	c.Assert(err, chk.IsNil)
   535  	z, ok = meta.Tags[tagCreateTag]
   536  	c.Assert(ok, chk.Equals, true)
   537  	c.Check(z.Commit, chk.Equals, tagCreateCommit)
   538  	c.Check(z.Date, chk.Equals, time.Date(2019, time.March, 15, 18, 1, 5, 0, time.UTC))
   539  	c.Check(z.Description, chk.Equals, tagCreateMsg)
   540  	c.Check(z.TaggerName, chk.Equals, tagCreateName)
   541  	c.Check(z.TaggerEmail, chk.Equals, tagCreateEmail)
   542  
   543  	// Verify the output given to the user
   544  	c.Check(strings.TrimSpace(s.buf.String()), chk.Equals, "Tag creation succeeded")
   545  }
   546  
   547  func (s *DioSuite) Test0140_TagList(c *chk.C) {
   548  	// Retrieve the tag list
   549  	err := tagList([]string{s.dbName})
   550  	c.Assert(err, chk.IsNil)
   551  
   552  	// The tag created by the previous test should be listed
   553  	lines := bufio.NewScanner(&s.buf)
   554  	var tagFound bool
   555  	for lines.Scan() {
   556  		l := strings.TrimSpace(lines.Text())
   557  		if strings.HasPrefix(l, "*") {
   558  			p := strings.Split(lines.Text(), "'")
   559  			if len(p) > 2 && p[1] == tagCreateTag {
   560  				c.Check(p, chk.HasLen, 3)
   561  				tagFound = true
   562  			}
   563  		}
   564  	}
   565  	c.Check(tagFound, chk.Equals, true)
   566  }
   567  
   568  func (s *DioSuite) Test0150_TagRemove(c *chk.C) {
   569  	// Verify that (prior to this removal) the tag exists
   570  	meta, err := localFetchMetadata(s.dbName, false)
   571  	c.Assert(err, chk.IsNil)
   572  	_, ok := meta.Tags[tagCreateTag]
   573  	c.Assert(ok, chk.Equals, true)
   574  
   575  	// Remove the tag
   576  	tagRemoveTag = tagCreateTag
   577  	err = tagRemove([]string{s.dbName})
   578  	c.Check(err, chk.IsNil)
   579  
   580  	// Verify the tag has been removed
   581  	meta, err = localFetchMetadata(s.dbName, false)
   582  	c.Assert(err, chk.IsNil)
   583  	_, ok = meta.Tags[tagCreateTag]
   584  	c.Check(ok, chk.Equals, false)
   585  
   586  	// Verify the output given to the user
   587  	p := strings.Split(s.buf.String(), "'")
   588  	c.Check(strings.TrimSpace(p[1]), chk.Equals, tagCreateTag)
   589  }
   590  
   591  func (s *DioSuite) Test0160_ReleaseCreate(c *chk.C) {
   592  	// Check the release to be created doesn't yet exist
   593  	releaseCreateRelease = "testrelease1"
   594  	meta, err := localFetchMetadata(s.dbName, false)
   595  	c.Assert(err, chk.IsNil)
   596  	z, ok := meta.Releases[releaseCreateRelease]
   597  	c.Assert(ok, chk.Equals, false)
   598  
   599  	// Create the release
   600  	releaseCreateCommit = "59b72b78cb83bdba371438cb36950fe007265445a63068ae5586c9cc19203941"
   601  	releaseCreateCreatorEmail = "somereleaser@example.org"
   602  	releaseCreateCreatorName = "A test releaser"
   603  	releaseCreateMsg = "This is a test release"
   604  	releaseCreateReleaseDate = "2019-03-15T18:01:06Z"
   605  	err = releaseCreate([]string{s.dbName})
   606  	c.Assert(err, chk.IsNil)
   607  
   608  	// Check the release was created
   609  	meta, err = localFetchMetadata(s.dbName, false)
   610  	c.Assert(err, chk.IsNil)
   611  	z, ok = meta.Releases[releaseCreateRelease]
   612  	c.Assert(ok, chk.Equals, true)
   613  	c.Check(z.Commit, chk.Equals, releaseCreateCommit)
   614  	c.Check(z.Date, chk.Equals, time.Date(2019, time.March, 15, 18, 1, 6, 0, time.UTC))
   615  	c.Check(z.Description, chk.Equals, releaseCreateMsg)
   616  	c.Check(z.ReleaserName, chk.Equals, releaseCreateCreatorName)
   617  	c.Check(z.ReleaserEmail, chk.Equals, releaseCreateCreatorEmail)
   618  
   619  	// Verify the output given to the user
   620  	c.Check(strings.TrimSpace(s.buf.String()), chk.Equals, "Release creation succeeded")
   621  }
   622  
   623  func (s *DioSuite) Test0170_ReleaseList(c *chk.C) {
   624  	// Retrieve the release list
   625  	err := releaseList([]string{s.dbName})
   626  	c.Assert(err, chk.IsNil)
   627  
   628  	// The release created by the previous test should be listed
   629  	lines := bufio.NewScanner(&s.buf)
   630  	var relFound bool
   631  	for lines.Scan() {
   632  		l := strings.TrimSpace(lines.Text())
   633  		if strings.HasPrefix(l, "*") {
   634  			p := strings.Split(lines.Text(), "'")
   635  			if len(p) > 2 && p[1] == releaseCreateRelease {
   636  				c.Check(p, chk.HasLen, 3)
   637  				relFound = true
   638  			}
   639  		}
   640  	}
   641  	c.Check(relFound, chk.Equals, true)
   642  }
   643  
   644  func (s *DioSuite) Test0180_ReleaseRemove(c *chk.C) {
   645  	// Verify that (prior to this removal) the release exists
   646  	meta, err := localFetchMetadata(s.dbName, false)
   647  	c.Assert(err, chk.IsNil)
   648  	_, ok := meta.Releases[releaseCreateRelease]
   649  	c.Assert(ok, chk.Equals, true)
   650  
   651  	// Remove the release
   652  	releaseRemoveRelease = releaseCreateRelease
   653  	err = releaseRemove([]string{s.dbName})
   654  	c.Check(err, chk.IsNil)
   655  
   656  	// Verify the release has been removed
   657  	meta, err = localFetchMetadata(s.dbName, false)
   658  	c.Assert(err, chk.IsNil)
   659  	_, ok = meta.Releases[releaseCreateRelease]
   660  	c.Check(ok, chk.Equals, false)
   661  
   662  	// Verify the output given to the user
   663  	p := strings.Split(s.buf.String(), "'")
   664  	c.Check(strings.TrimSpace(p[1]), chk.Equals, releaseCreateRelease)
   665  }
   666  
   667  func (s *DioSuite) Test0190_Log(c *chk.C) {
   668  	// Retrieve the commit list
   669  	err := branchLog([]string{s.dbName})
   670  	c.Assert(err, chk.IsNil)
   671  
   672  	// The original commit should be listed
   673  	lines := bufio.NewScanner(&s.buf)
   674  	var comFound bool
   675  	for lines.Scan() {
   676  		l := strings.TrimSpace(lines.Text())
   677  		if strings.HasPrefix(l, "*") {
   678  			p := strings.Split(lines.Text(), ":")
   679  			if len(p) >= 2 && strings.TrimSpace(p[1]) == "59b72b78cb83bdba371438cb36950fe007265445a63068ae5586c9cc19203941" {
   680  				c.Check(p, chk.HasLen, 2)
   681  				comFound = true
   682  			}
   683  		}
   684  	}
   685  	c.Check(comFound, chk.Equals, true)
   686  }
   687  
   688  func (s *DioSuite) Test0200_StatusUnchanged(c *chk.C) {
   689  	// Run the status check command
   690  	err := status([]string{s.dbName})
   691  	c.Assert(err, chk.IsNil)
   692  
   693  	// Verify the output
   694  	lines := bufio.NewScanner(&s.buf)
   695  	numEntries := 0
   696  	unchangedFound := false
   697  	for lines.Scan() {
   698  		l := strings.TrimSpace(lines.Text())
   699  		if strings.HasPrefix(l, "*") {
   700  			numEntries++
   701  			p := strings.Split(lines.Text(), "'")
   702  			if len(p) >= 2 && strings.TrimSpace(p[1]) == s.dbName {
   703  				c.Check(p, chk.HasLen, 3)
   704  				if strings.HasSuffix(p[2], "unchanged") {
   705  					unchangedFound = true
   706  				}
   707  			}
   708  		}
   709  	}
   710  	c.Check(numEntries, chk.Equals, 1)
   711  	c.Check(unchangedFound, chk.Equals, true)
   712  }
   713  
   714  func (s *DioSuite) Test0210_StatusChanged(c *chk.C) {
   715  	// Get the current last modified date for the database file
   716  	f, err := os.Stat(s.dbFile)
   717  	c.Assert(err, chk.IsNil)
   718  	lastMod := f.ModTime()
   719  
   720  	// Update last modified date on the database file
   721  	err = os.Chtimes(s.dbFile, time.Now(), time.Date(2019, time.March, 15, 18, 1, 8, 0, time.UTC))
   722  	c.Assert(err, chk.IsNil)
   723  
   724  	// Run the status check command
   725  	err = status([]string{s.dbName})
   726  	c.Assert(err, chk.IsNil)
   727  
   728  	// Verify the output
   729  	lines := bufio.NewScanner(&s.buf)
   730  	numEntries := 0
   731  	changedFound := false
   732  	for lines.Scan() {
   733  		l := strings.TrimSpace(lines.Text())
   734  		if strings.HasPrefix(l, "*") {
   735  			numEntries++
   736  			p := strings.Split(lines.Text(), "'")
   737  			if len(p) >= 2 && strings.TrimSpace(p[1]) == s.dbName {
   738  				c.Check(p, chk.HasLen, 3)
   739  				if strings.HasSuffix(p[2], "changed") {
   740  					changedFound = true
   741  				}
   742  			}
   743  		}
   744  	}
   745  	c.Check(numEntries, chk.Equals, 1)
   746  	c.Check(changedFound, chk.Equals, true)
   747  
   748  	// Restore the last modified date on the database file
   749  	err = os.Chtimes(s.dbFile, time.Now(), lastMod)
   750  	c.Assert(err, chk.IsNil)
   751  }
   752  
   753  func (s *DioSuite) Test0220_LicenceList(c *chk.C) {
   754  	// Retrieve the licence list
   755  	err := licenceList()
   756  	c.Assert(err, chk.IsNil)
   757  
   758  	// Make sure an entry for "No licence specified" is given on the output
   759  	numEntries := 0
   760  	licFound := false
   761  	lines := bufio.NewScanner(&s.buf)
   762  	for lines.Scan() {
   763  		l := strings.TrimSpace(lines.Text())
   764  		if strings.HasPrefix(l, "*") {
   765  			numEntries++
   766  			p := strings.Split(lines.Text(), ":")
   767  			if len(p) >= 2 && strings.TrimSpace(p[1]) == "No licence specified" {
   768  				c.Check(p, chk.HasLen, 2)
   769  				licFound = true
   770  			}
   771  		}
   772  	}
   773  	c.Check(licFound, chk.Equals, true)
   774  }
   775  
   776  func (s *DioSuite) Test0230_LicenceAdd(c *chk.C) {
   777  	// Add a licence to the testing server
   778  	licenceAddDisplayOrder = 2000
   779  	licenceAddFileFormat = "text"
   780  	licenceAddFullName = "GNU AFFERO GENERAL PUBLIC LICENSE"
   781  	licenceAddFile = licFile
   782  	licenceAddURL = "https://www.gnu.org/licenses/agpl-3.0.en.html"
   783  	err := licenceAdd([]string{"AGPL3"})
   784  	c.Assert(err, chk.IsNil)
   785  
   786  	// Calculate the SHA256 of the licence file
   787  	b, err := os.ReadFile(licFile)
   788  	c.Assert(err, chk.IsNil)
   789  	z := sha256.Sum256(b)
   790  	shaSum := hex.EncodeToString(z[:])
   791  
   792  	// Verify the info sent via licenceAdd
   793  	licList, err := getLicences()
   794  	c.Assert(err, chk.IsNil)
   795  
   796  	licVerify := licList["AGPL3"]
   797  	c.Assert(licVerify.FileFormat, chk.Equals, licenceAddFileFormat)
   798  	c.Assert(licVerify.FullName, chk.Equals, licenceAddFullName)
   799  	c.Assert(licVerify.Order, chk.Equals, licenceAddDisplayOrder)
   800  	c.Assert(licVerify.Sha256, chk.Equals, shaSum)
   801  	c.Assert(licVerify.URL, chk.Equals, licenceAddURL)
   802  }
   803  
   804  func (s *DioSuite) Test0240_LicenceGet(c *chk.C) {
   805  	// Calculate the SHA256 of the original licence file
   806  	b, err := os.ReadFile(licFile)
   807  	c.Assert(err, chk.IsNil)
   808  	z := sha256.Sum256(b)
   809  	origSHASum := hex.EncodeToString(z[:])
   810  
   811  	// Make sure "AGPL3.txt" doesn't exist in the current directory before the call to licenceGet()
   812  	getFile := filepath.Join(tempDir, "AGPL3.txt")
   813  	_, err = os.Stat(getFile)
   814  	c.Assert(err, chk.Not(chk.IsNil))
   815  
   816  	// Retrieve the AGPL3 licence from the test server using licenceGet()
   817  	err = licenceGet([]string{"AGPL3"})
   818  	c.Assert(err, chk.IsNil)
   819  
   820  	// Verify the AGPL3.txt licence now exists, and its contents match what's expected
   821  	_, err = os.Stat(getFile)
   822  	c.Assert(err, chk.IsNil)
   823  	y, err := os.ReadFile(licFile)
   824  	c.Assert(err, chk.IsNil)
   825  	z = sha256.Sum256(y)
   826  	newSHASum := hex.EncodeToString(z[:])
   827  	c.Check(newSHASum, chk.Equals, origSHASum)
   828  }
   829  
   830  func (s *DioSuite) Test0250_LicenceRemove(c *chk.C) {
   831  	// Verify the AGPL3 licence exists on the test server prior to our licenceRemove() call
   832  	oldEntries := 0
   833  	licFound := false
   834  	err := licenceList()
   835  	c.Assert(err, chk.IsNil)
   836  	lines := bufio.NewScanner(&s.buf)
   837  	for lines.Scan() {
   838  		l := strings.TrimSpace(lines.Text())
   839  		if strings.HasPrefix(l, "*") {
   840  			oldEntries++
   841  			p := strings.Split(lines.Text(), ":")
   842  			if len(p) >= 2 && strings.TrimSpace(p[1]) == "GNU AFFERO GENERAL PUBLIC LICENSE" {
   843  				c.Check(p, chk.HasLen, 2)
   844  				licFound = true
   845  			}
   846  		}
   847  	}
   848  	c.Check(licFound, chk.Equals, true)
   849  
   850  	// Run the licence removal call
   851  	err = licenceRemove([]string{"AGPL3"})
   852  	c.Assert(err, chk.IsNil)
   853  
   854  	// Verify the licence has been removed
   855  	err = licenceList()
   856  	c.Assert(err, chk.IsNil)
   857  	numEntries := 0
   858  	licFound = false
   859  	lines = bufio.NewScanner(&s.buf)
   860  	for lines.Scan() {
   861  		l := strings.TrimSpace(lines.Text())
   862  		if strings.HasPrefix(l, "*") {
   863  			numEntries++
   864  			p := strings.Split(lines.Text(), ":")
   865  			if len(p) >= 2 && strings.TrimSpace(p[1]) == "AGPL3" {
   866  				c.Check(p, chk.HasLen, 2)
   867  				licFound = true
   868  			}
   869  		}
   870  	}
   871  	c.Check(numEntries, chk.Equals, oldEntries-1)
   872  	c.Check(licFound, chk.Equals, false)
   873  }
   874  
   875  func (s *DioSuite) Test0260_PullLocal(c *chk.C) {
   876  	// Calculate the SHA256 of the test database
   877  	b, err := os.ReadFile(s.dbFile)
   878  	c.Assert(err, chk.IsNil)
   879  	z := sha256.Sum256(b)
   880  	origSHASum := hex.EncodeToString(z[:])
   881  
   882  	// Remove the local copy of our test database
   883  	err = os.Remove(s.dbFile)
   884  	c.Assert(err, chk.IsNil)
   885  
   886  	// Grab the database from local cache
   887  	pullCmdBranch = "main"
   888  	pullCmdCommit = ""
   889  	*pullForce = true
   890  	err = pull([]string{s.dbName})
   891  	c.Assert(err, chk.IsNil)
   892  
   893  	// Verify the SHA256 of the retrieved database matches
   894  	b, err = os.ReadFile(s.dbFile)
   895  	c.Assert(err, chk.IsNil)
   896  	z = sha256.Sum256(b)
   897  	newSHASum := hex.EncodeToString(z[:])
   898  	c.Check(newSHASum, chk.Equals, origSHASum)
   899  }
   900  
   901  // Tests pushing a database with no local commit data, and which doesn't yet exist on the remote server (should succeed)
   902  func (s *DioSuite) Test0270_PushCompletelyNewDB(c *chk.C) {
   903  	// Make sure the new database isn't yet shown on the remote server
   904  	newDB := "19kBv2.sqlite"
   905  	dbList, err := getDatabases(cloud, "default")
   906  	c.Assert(err, chk.IsNil)
   907  	dbFound := false
   908  	for _, j := range dbList {
   909  		if j.Name == newDB {
   910  			dbFound = true
   911  		}
   912  	}
   913  	c.Assert(dbFound, chk.Equals, false)
   914  
   915  	// Rename our test database from 19kB.sqlite to 19kBv2.sqlite, to give us a "new" database with no local metadata
   916  	err = os.Rename(s.dbFile, newDB)
   917  	c.Assert(err, chk.IsNil)
   918  	err = os.Chtimes(newDB, time.Now(), time.Date(2019, time.March, 15, 18, 2, 0, 0, time.UTC))
   919  	c.Assert(err, chk.IsNil)
   920  
   921  	// Send the test database to the server
   922  	pushCmdName = "Default test user"
   923  	pushCmdBranch = "main"
   924  	pushCmdCommit = ""
   925  	pushCmdDB = newDB
   926  	pushCmdEmail = "testdefault@dbhub.io"
   927  	pushCmdForce = false
   928  	pushCmdLicence = "Not specified"
   929  	pushCmdMsg = "Test message"
   930  	pushCmdPublic = false
   931  	pushCmdTimestamp = time.Date(2019, time.March, 15, 18, 30, 0, 0, time.UTC).Format(time.RFC3339)
   932  	err = push([]string{newDB})
   933  	c.Assert(err, chk.IsNil)
   934  
   935  	// Verify the new test database is on the server
   936  	dbList, err = getDatabases(cloud, "default")
   937  	c.Assert(err, chk.IsNil)
   938  	dbFound = false
   939  	for _, j := range dbList {
   940  		if j.Name == newDB {
   941  			dbFound = true
   942  		}
   943  	}
   944  	c.Assert(dbFound, chk.Equals, true)
   945  }
   946  
   947  func (s *DioSuite) Test0280_PullRemote(c *chk.C) {
   948  	// Calculate the SHA256 of the test database
   949  	newDB := "19kBv2.sqlite"
   950  	b, err := os.ReadFile(newDB)
   951  	c.Assert(err, chk.IsNil)
   952  	z := sha256.Sum256(b)
   953  	origSHASum := hex.EncodeToString(z[:])
   954  
   955  	// Rename the local copy of our test database
   956  	err = os.Rename(newDB, newDB+"-renamed")
   957  	c.Assert(err, chk.IsNil)
   958  
   959  	// Remove the local metadata and cache for our test database
   960  	metaDir := filepath.Join(".dio", newDB)
   961  	err = os.RemoveAll(metaDir)
   962  	c.Assert(err, chk.IsNil)
   963  
   964  	// Grab the database from our test server
   965  	pullCmdBranch = ""
   966  	pullCmdCommit = "9a78d0c8c13c0442eb24367f3561d0cbb5676100bd69d147ec3bde4cdbaefa49"
   967  	*pullForce = true
   968  	err = pull([]string{newDB})
   969  	c.Assert(err, chk.IsNil)
   970  
   971  	// Verify the SHA256 of the retrieved database matches
   972  	b, err = os.ReadFile(newDB)
   973  	c.Assert(err, chk.IsNil)
   974  	z = sha256.Sum256(b)
   975  	newSHASum := hex.EncodeToString(z[:])
   976  	c.Check(newSHASum, chk.Equals, origSHASum)
   977  }
   978  
   979  // Tests pushing a database with no local commit data, but already exists on the remote server (should fail)
   980  func (s *DioSuite) Test0290_PushExistingDBConflict(c *chk.C) {
   981  	// Verify 19kBv2.sqlite exists on the test server
   982  	newDB := "19kBv2.sqlite"
   983  	dbList, err := getDatabases(cloud, "default")
   984  	c.Assert(err, chk.IsNil)
   985  	dbFound := false
   986  	for _, j := range dbList {
   987  		if j.Name == newDB {
   988  			dbFound = true
   989  		}
   990  	}
   991  	c.Assert(dbFound, chk.Equals, true)
   992  
   993  	// Remove the local metadata for 19kBv2.sqlite
   994  	metaDir := filepath.Join(".dio", newDB)
   995  	err = os.RemoveAll(metaDir)
   996  	c.Assert(err, chk.IsNil)
   997  
   998  	// Push the database to the test server (should fail)
   999  	pushCmdName = "Default test user"
  1000  	pushCmdBranch = "main"
  1001  	pushCmdCommit = ""
  1002  	pushCmdDB = newDB
  1003  	pushCmdEmail = "testdefault@dbhub.io"
  1004  	pushCmdForce = false
  1005  	pushCmdLicence = "Not specified"
  1006  	pushCmdMsg = "Test message"
  1007  	pushCmdPublic = false
  1008  	err = push([]string{newDB})
  1009  	c.Check(err, chk.Not(chk.IsNil))
  1010  }
  1011  
  1012  // Tests pushing a database with local commit data, which doesn't yet exist on the remote server (should succeed)
  1013  func (s *DioSuite) Test0300_PushNewLocalDBAndMetadata(c *chk.C) {
  1014  	// Rename the test database to "19kBv3.sqlite"
  1015  	newDB := "19kBv3.sqlite"
  1016  	err := os.Rename("19kBv2.sqlite", newDB)
  1017  	c.Assert(err, chk.IsNil)
  1018  	err = os.Chtimes(newDB, time.Now(), time.Date(2019, time.March, 15, 18, 1, 10, 0, time.UTC))
  1019  	c.Assert(err, chk.IsNil)
  1020  
  1021  	// Create a commit using the new test database
  1022  	commitCmdBranch = "main"
  1023  	commitCmdCommit = ""
  1024  	commitCmdAuthEmail = "testdefault@dbhub.io"
  1025  	commitCmdLicence = "Not specified"
  1026  	commitCmdMsg = "Test message"
  1027  	commitCmdAuthName = "Default test user"
  1028  	commitCmdTimestamp = time.Date(2019, time.March, 15, 18, 1, 10, 0, time.UTC).Format(time.RFC3339)
  1029  	err = commit([]string{newDB})
  1030  
  1031  	// Verify the database doesn't exist remotely yet
  1032  	dbList, err := getDatabases(cloud, "default")
  1033  	c.Assert(err, chk.IsNil)
  1034  	dbFound := false
  1035  	for _, j := range dbList {
  1036  		if j.Name == newDB {
  1037  			dbFound = true
  1038  		}
  1039  	}
  1040  	c.Assert(dbFound, chk.Equals, false)
  1041  
  1042  	// Push the database to the server
  1043  	pushCmdName = ""
  1044  	pushCmdBranch = ""
  1045  	pushCmdCommit = ""
  1046  	pushCmdDB = newDB
  1047  	pushCmdEmail = ""
  1048  	pushCmdForce = false
  1049  	pushCmdLicence = ""
  1050  	pushCmdMsg = ""
  1051  	pushCmdPublic = false
  1052  	err = push([]string{newDB})
  1053  	c.Check(err, chk.IsNil)
  1054  
  1055  	// Verify the database has been created
  1056  	dbList, err = getDatabases(cloud, "default")
  1057  	c.Assert(err, chk.IsNil)
  1058  	dbFound = false
  1059  	for _, j := range dbList {
  1060  		if j.Name == newDB {
  1061  			dbFound = true
  1062  		}
  1063  	}
  1064  	c.Assert(dbFound, chk.Equals, true)
  1065  }
  1066  
  1067  // Tests pushing a database with local commit data, which already exists remotely, and the local commits add to the remote (should succeed)
  1068  func (s *DioSuite) Test0310_PushLocalDBAndMetadata(c *chk.C) {
  1069  	// Create another local commit for 19kbv3.sqlite
  1070  	newDB := "19kBv3.sqlite"
  1071  	err := os.Chtimes(newDB, time.Now(), time.Date(2019, time.March, 15, 18, 11, 0, 0, time.UTC))
  1072  	c.Assert(err, chk.IsNil)
  1073  	commitCmdMsg = "Test message"
  1074  	commitCmdTimestamp = time.Date(2019, time.March, 15, 18, 1, 10, 0, time.UTC).Format(time.RFC3339)
  1075  	err = commit([]string{newDB})
  1076  	c.Check(err, chk.IsNil)
  1077  
  1078  	// Push the new commit to the server
  1079  	pushCmdName = "Default test user"
  1080  	pushCmdBranch = "main"
  1081  	pushCmdCommit = ""
  1082  	pushCmdDB = s.dbName
  1083  	pushCmdEmail = "testdefault@dbhub.io"
  1084  	pushCmdForce = false
  1085  	pushCmdLicence = "Not specified"
  1086  	pushCmdMsg = "Test message"
  1087  	pushCmdPublic = false
  1088  	err = push([]string{newDB})
  1089  	c.Check(err, chk.IsNil)
  1090  
  1091  	// Verify the server now has two commits for the database
  1092  	meta, _, err := retrieveMetadata(newDB)
  1093  	c.Assert(err, chk.IsNil)
  1094  	c.Check(meta.Commits, chk.HasLen, 2)
  1095  }
  1096  
  1097  // Tests pushing a database with local commit data, which already exists remotely, and the local commits are behind the remote (should fail)
  1098  func (s *DioSuite) Test0320_PushOldLocalMetadataDBConflict(c *chk.C) {
  1099  	// We use the same 19bKv3.sqlite database from the previous test, reverting it back to it's original commit, then pushing
  1100  
  1101  	// Revert back to the original commit
  1102  	newDB := "19kBv3.sqlite"
  1103  	pullCmdCommit = "601e78fb16a715e37e86fc57ba3415e2684481cffea0455eb3463dc086e22177"
  1104  	*pullForce = true
  1105  	err := pull([]string{newDB})
  1106  	c.Assert(err, chk.IsNil)
  1107  
  1108  	// Push the database to the server
  1109  	pushCmdName = ""
  1110  	pushCmdBranch = ""
  1111  	pushCmdCommit = ""
  1112  	pushCmdDB = newDB
  1113  	pushCmdEmail = ""
  1114  	pushCmdForce = false
  1115  	pushCmdLicence = ""
  1116  	pushCmdMsg = ""
  1117  	pushCmdPublic = false
  1118  	err = push([]string{newDB})
  1119  	c.Check(err, chk.Not(chk.IsNil))
  1120  }
  1121  
  1122  // genTestCert retrieves a client certificate from the remote server
  1123  func genTestCert(server, outputPath string) (err error) {
  1124  	// Disable https cert validation for our tests
  1125  	insecureTLS := tls.Config{InsecureSkipVerify: true}
  1126  	insecureTransport := http.Transport{TLSClientConfig: &insecureTLS}
  1127  	client := http.Client{Transport: &insecureTransport}
  1128  
  1129  	// Request the new client certificate
  1130  	resp, err := client.Get(strings.Replace(server, "5550", "9443", 1) + "/x/test/gencert")
  1131  	if err != nil {
  1132  		return
  1133  	}
  1134  	if resp.StatusCode != 200 {
  1135  		err = fmt.Errorf("client certificate generation request failure. http code '%d' returned", resp.StatusCode)
  1136  		return
  1137  	}
  1138  
  1139  	// Write the certificate to the filesystem
  1140  	rawCert, err := io.ReadAll(resp.Body)
  1141  	if err != nil {
  1142  		return
  1143  	}
  1144  	err = os.WriteFile(outputPath, rawCert, 0640)
  1145  	if err != nil {
  1146  		return
  1147  	}
  1148  	return
  1149  }
  1150  
  1151  // seedTests retrieves a client certificate from the remote server
  1152  func seedTests(server string) (err error) {
  1153  	// Disable https cert validation for our tests
  1154  	insecureTLS := tls.Config{InsecureSkipVerify: true}
  1155  	insecureTransport := http.Transport{TLSClientConfig: &insecureTLS}
  1156  	client := http.Client{Transport: &insecureTransport}
  1157  
  1158  	// Seed the database
  1159  	resp, err := client.Get(strings.Replace(server, "5550", "9443", 1) + "/x/test/seed")
  1160  	if err != nil {
  1161  		return
  1162  	}
  1163  	if resp.StatusCode != 200 {
  1164  		err = fmt.Errorf("seeding database failed. http code '%d' returned", resp.StatusCode)
  1165  		return
  1166  	}
  1167  	return
  1168  }