github.com/bartle-stripe/trillian@v1.2.1/storage/mysql/admin_storage_test.go (about)

     1  // Copyright 2017 Google Inc. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package mysql
    16  
    17  import (
    18  	"context"
    19  	"database/sql"
    20  	"fmt"
    21  	"testing"
    22  
    23  	"github.com/golang/protobuf/proto"
    24  	"github.com/golang/protobuf/ptypes"
    25  	"github.com/google/trillian"
    26  	"github.com/google/trillian/crypto/keyspb"
    27  	"github.com/google/trillian/storage"
    28  	"github.com/google/trillian/storage/testonly"
    29  )
    30  
    31  const selectTreeControlByID = "SELECT SigningEnabled, SequencingEnabled, SequenceIntervalSeconds FROM TreeControl WHERE TreeId = ?"
    32  
    33  func TestMysqlAdminStorage(t *testing.T) {
    34  	tester := &testonly.AdminStorageTester{NewAdminStorage: func() storage.AdminStorage {
    35  		cleanTestDB(DB)
    36  		return NewAdminStorage(DB)
    37  	}}
    38  	tester.RunAllTests(t)
    39  }
    40  
    41  func TestAdminTX_CreateTree_InitializesStorageStructures(t *testing.T) {
    42  	cleanTestDB(DB)
    43  	s := NewAdminStorage(DB)
    44  	ctx := context.Background()
    45  
    46  	tree, err := storage.CreateTree(ctx, s, testonly.LogTree)
    47  	if err != nil {
    48  		t.Fatalf("CreateTree() failed: %v", err)
    49  	}
    50  
    51  	// Check if TreeControl is correctly written.
    52  	var signingEnabled, sequencingEnabled bool
    53  	var sequenceIntervalSeconds int
    54  	if err := DB.QueryRowContext(ctx, selectTreeControlByID, tree.TreeId).Scan(&signingEnabled, &sequencingEnabled, &sequenceIntervalSeconds); err != nil {
    55  		t.Fatalf("Failed to read TreeControl: %v", err)
    56  	}
    57  	// We don't mind about specific values, defaults change, but let's check
    58  	// that important numbers are not zeroed.
    59  	if sequenceIntervalSeconds <= 0 {
    60  		t.Errorf("sequenceIntervalSeconds = %v, want > 0", sequenceIntervalSeconds)
    61  	}
    62  }
    63  
    64  func TestCreateTreeInvalidStates(t *testing.T) {
    65  	cleanTestDB(DB)
    66  	s := NewAdminStorage(DB)
    67  	ctx := context.Background()
    68  
    69  	states := []trillian.TreeState{trillian.TreeState_DRAINING, trillian.TreeState_FROZEN}
    70  
    71  	for _, state := range states {
    72  		inTree := proto.Clone(testonly.LogTree).(*trillian.Tree)
    73  		inTree.TreeState = state
    74  		if _, err := storage.CreateTree(ctx, s, inTree); err == nil {
    75  			t.Errorf("CreateTree() state: %v got: nil want: err", state)
    76  		}
    77  	}
    78  }
    79  
    80  func TestAdminTX_TreeWithNulls(t *testing.T) {
    81  	cleanTestDB(DB)
    82  	s := NewAdminStorage(DB)
    83  	ctx := context.Background()
    84  
    85  	// Setup: create a tree and set all nullable columns to null.
    86  	// Some columns have to be manually updated, as it's not possible to set
    87  	// some proto fields to nil.
    88  	tree, err := storage.CreateTree(ctx, s, testonly.LogTree)
    89  	if err != nil {
    90  		t.Fatalf("CreateTree() failed: %v", err)
    91  	}
    92  	treeID := tree.TreeId
    93  
    94  	if err := setNulls(ctx, DB, treeID); err != nil {
    95  		t.Fatalf("setNulls() = %v, want = nil", err)
    96  	}
    97  
    98  	tests := []struct {
    99  		desc string
   100  		fn   storage.AdminTXFunc
   101  	}{
   102  		{
   103  			desc: "GetTree",
   104  			fn: func(ctx context.Context, tx storage.AdminTX) error {
   105  				_, err := tx.GetTree(ctx, treeID)
   106  				return err
   107  			},
   108  		},
   109  		{
   110  			// ListTreeIDs *shouldn't* care about other columns, but let's test it just
   111  			// in case.
   112  			desc: "ListTreeIDs",
   113  			fn: func(ctx context.Context, tx storage.AdminTX) error {
   114  				ids, err := tx.ListTreeIDs(ctx, false /* includeDeleted */)
   115  				if err != nil {
   116  					return err
   117  				}
   118  				for _, id := range ids {
   119  					if id == treeID {
   120  						return nil
   121  					}
   122  				}
   123  				return fmt.Errorf("ID not found: %v", treeID)
   124  			},
   125  		},
   126  		{
   127  			desc: "ListTrees",
   128  			fn: func(ctx context.Context, tx storage.AdminTX) error {
   129  				trees, err := tx.ListTrees(ctx, false /* includeDeleted */)
   130  				if err != nil {
   131  					return err
   132  				}
   133  				for _, tree := range trees {
   134  					if tree.TreeId == treeID {
   135  						return nil
   136  					}
   137  				}
   138  				return fmt.Errorf("ID not found: %v", treeID)
   139  			},
   140  		},
   141  	}
   142  	for _, test := range tests {
   143  		if err := s.ReadWriteTransaction(ctx, test.fn); err != nil {
   144  			t.Errorf("%v: err = %v, want = nil", test.desc, err)
   145  		}
   146  	}
   147  }
   148  
   149  func TestAdminTX_StorageSettingsNotSupported(t *testing.T) {
   150  	cleanTestDB(DB)
   151  	s := NewAdminStorage(DB)
   152  	ctx := context.Background()
   153  
   154  	settings, err := ptypes.MarshalAny(&keyspb.PEMKeyFile{})
   155  	if err != nil {
   156  		t.Fatalf("Error marshaling proto: %v", err)
   157  	}
   158  
   159  	tests := []struct {
   160  		desc string
   161  		// fn attempts to either create or update a tree with a non-nil, valid Any proto
   162  		// on Tree.StorageSettings. It's expected to return an error.
   163  		fn func(storage.AdminStorage) error
   164  	}{
   165  		{
   166  			desc: "CreateTree",
   167  			fn: func(s storage.AdminStorage) error {
   168  				tree := *testonly.LogTree
   169  				tree.StorageSettings = settings
   170  				_, err := storage.CreateTree(ctx, s, &tree)
   171  				return err
   172  			},
   173  		},
   174  		{
   175  			desc: "UpdateTree",
   176  			fn: func(s storage.AdminStorage) error {
   177  				tree, err := storage.CreateTree(ctx, s, testonly.LogTree)
   178  				if err != nil {
   179  					t.Fatalf("CreateTree() failed with err = %v", err)
   180  				}
   181  				_, err = storage.UpdateTree(ctx, s, tree.TreeId, func(tree *trillian.Tree) { tree.StorageSettings = settings })
   182  				return err
   183  			},
   184  		},
   185  	}
   186  	for _, test := range tests {
   187  		if err := test.fn(s); err == nil {
   188  			t.Errorf("%v: err = nil, want non-nil", test.desc)
   189  		}
   190  	}
   191  }
   192  
   193  func TestAdminTX_HardDeleteTree(t *testing.T) {
   194  	cleanTestDB(DB)
   195  	s := NewAdminStorage(DB)
   196  	ctx := context.Background()
   197  
   198  	tree, err := storage.CreateTree(ctx, s, testonly.LogTree)
   199  	if err != nil {
   200  		t.Fatalf("CreateTree() returned err = %v", err)
   201  	}
   202  
   203  	if err := s.ReadWriteTransaction(ctx, func(ctx context.Context, tx storage.AdminTX) error {
   204  		if _, err := tx.SoftDeleteTree(ctx, tree.TreeId); err != nil {
   205  			return err
   206  		}
   207  		return tx.HardDeleteTree(ctx, tree.TreeId)
   208  	}); err != nil {
   209  		t.Fatalf("ReadWriteTransaction() returned err = %v", err)
   210  	}
   211  
   212  	// Unlike the HardDelete tests on AdminStorageTester, here we have the chance to poke inside the
   213  	// database and check that the rows are gone, so let's do just that.
   214  	// If there's no record on Trees, then there can be no record in any of the dependent tables.
   215  	var name string
   216  	if err := DB.QueryRowContext(ctx, "SELECT DisplayName FROM Trees WHERE TreeId = ?", tree.TreeId).Scan(&name); err != sql.ErrNoRows {
   217  		t.Errorf("QueryRowContext() returned err = %v, want = %v", err, sql.ErrNoRows)
   218  	}
   219  }
   220  
   221  func TestCheckDatabaseAccessible_Fails(t *testing.T) {
   222  	// Pass in a closed database to provoke a failure.
   223  	db := openTestDBOrDie()
   224  	cleanTestDB(db)
   225  	s := NewAdminStorage(db)
   226  	db.Close()
   227  	ctx := context.Background()
   228  	if err := s.CheckDatabaseAccessible(ctx); err == nil {
   229  		t.Error("TestCheckDatabaseAccessible_Fails got: nil, want: err")
   230  	}
   231  }
   232  
   233  func TestCheckDatabaseAccessible_OK(t *testing.T) {
   234  	cleanTestDB(DB)
   235  	s := NewAdminStorage(DB)
   236  	ctx := context.Background()
   237  	if err := s.CheckDatabaseAccessible(ctx); err != nil {
   238  		t.Errorf("TestCheckDatabaseAccessible_OK got: %v, want: nil", err)
   239  	}
   240  }
   241  
   242  func setNulls(ctx context.Context, db *sql.DB, treeID int64) error {
   243  	stmt, err := db.PrepareContext(ctx, "UPDATE Trees SET DisplayName = NULL, Description = NULL WHERE TreeId = ?")
   244  	if err != nil {
   245  		return err
   246  	}
   247  	defer stmt.Close()
   248  	_, err = stmt.ExecContext(ctx, treeID)
   249  	return err
   250  }