Skip to content

Commit

Permalink
Merge pull request #899 from kaleido-io/e2e
Browse files Browse the repository at this point in the history
Add E2E tests for contract termination
  • Loading branch information
awrichar authored Jul 26, 2022
2 parents ba355d8 + eb2afa8 commit f0a2f26
Show file tree
Hide file tree
Showing 65 changed files with 1,865 additions and 676 deletions.
6 changes: 3 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ smart_contracts/ethereum/solidity_firefly/build
smart_contracts/ethereum/solidity_firefly/package-lock.json
utils/kat-client/node_modules
**/*.jar
firefly
firefly-nocgo
/firefly
/firefly-nocgo
coverage.txt
**/debug.test
.DS_Store
frontend
__debug*
!deploy/charts/firefly
containerlogs
.vscode/*.log
.vscode/*.log
30 changes: 15 additions & 15 deletions cmd/firefly.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,35 +118,35 @@ func run() error {
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

for {
log.L(ctx).Infof("Starting up")
rootCtx, rootCancelCtx := context.WithCancel(ctx)
mgr := getRootManager()
as := apiserver.NewAPIServer()
go startFirefly(ctx, mgr, as, errChan)
go startFirefly(rootCtx, rootCancelCtx, mgr, as, errChan)
select {
case sig := <-sigs:
log.L(ctx).Infof("Shutting down due to %s", sig.String())
cancelCtx()
mgr.WaitStop()
return nil
/*
// TODO: seems useful, but there's currently no way to trigger this
case <-ctx.Done():
log.L(ctx).Infof("Restarting due to configuration change")
mgr.WaitStop()
// Re-read the configuration
coreconfig.Reset()
if err := config.ReadConfig(configSuffix, cfgFile); err != nil {
cancelCtx()
return err
}
*/
case <-rootCtx.Done():
log.L(ctx).Infof("Restarting due to configuration change")
mgr.WaitStop()
// Re-read the configuration
coreconfig.Reset()
apiserver.InitConfig()
if err := config.ReadConfig(configSuffix, cfgFile); err != nil {
cancelCtx()
return err
}
case err := <-errChan:
cancelCtx()
return err
}
}
}

func startFirefly(ctx context.Context, mgr namespace.Manager, as apiserver.Server, errChan chan error) {
func startFirefly(ctx context.Context, cancelCtx context.CancelFunc, mgr namespace.Manager, as apiserver.Server, errChan chan error) {
var err error
// Start debug listener
debugPort := config.GetInt(coreconfig.DebugPort)
Expand All @@ -163,7 +163,7 @@ func startFirefly(ctx context.Context, mgr namespace.Manager, as apiserver.Serve
log.L(ctx).Debugf("Debug HTTP endpoint listening on localhost:%d", debugPort)
}

if err = mgr.Init(ctx); err != nil {
if err = mgr.Init(ctx, cancelCtx); err != nil {
errChan <- err
return
}
Expand Down
54 changes: 53 additions & 1 deletion cmd/firefly_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,58 @@ func TestExecOkExitSIGINT(t *testing.T) {
assert.NoError(t, err)
}

func TestExecOkRestartThenExit(t *testing.T) {
o := &namespacemocks.Manager{}
var orContext context.Context
initCount := 0
init := o.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
init.RunFn = func(a mock.Arguments) {
orContext = a[0].(context.Context)
cancelOrContext := a[1].(context.CancelFunc)
initCount++
if initCount == 2 {
init.ReturnArguments = mock.Arguments{fmt.Errorf("second run")}
}
cancelOrContext()
}
o.On("Start").Return(nil)
ws := o.On("WaitStop")
ws.RunFn = func(a mock.Arguments) {
<-orContext.Done()
}
_utManager = o
defer func() { _utManager = nil }()

os.Chdir(configDir)
err := Execute()
assert.EqualError(t, err, "second run")
}

func TestExecOkRestartConfigProblem(t *testing.T) {
o := &namespacemocks.Manager{}
tmpDir, err := os.MkdirTemp(os.TempDir(), "ut")
assert.NoError(t, err)
defer os.RemoveAll(tmpDir)
var orContext context.Context
init := o.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
init.RunFn = func(a mock.Arguments) {
orContext = a[0].(context.Context)
cancelOrContext := a[1].(context.CancelFunc)
cancelOrContext()
}
o.On("Start").Return(nil)
o.On("WaitStop").Run(func(args mock.Arguments) {
<-orContext.Done()
os.Chdir(tmpDir) // this will mean we fail to read the config
})
_utManager = o
defer func() { _utManager = nil }()

os.Chdir(configDir)
err = Execute()
assert.Regexp(t, "Config File.*Not Found", err)
}

func TestAPIServerError(t *testing.T) {
o := &namespacemocks.Manager{}
o.On("Init", mock.Anything, mock.Anything).Return(nil)
Expand All @@ -99,7 +151,7 @@ func TestAPIServerError(t *testing.T) {
as.On("Serve", mock.Anything, o).Return(fmt.Errorf("pop"))

errChan := make(chan error)
go startFirefly(context.Background(), o, as, errChan)
go startFirefly(context.Background(), func() {}, o, as, errChan)
err := <-errChan
assert.EqualError(t, err, "pop")
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,9 @@ ALTER TABLE namespaces ADD COLUMN id UUID;
ALTER TABLE namespaces ADD COLUMN message_id UUID;
ALTER TABLE namespaces ADD COLUMN ntype VARCHAR(64);
ALTER TABLE namespaces DROP COLUMN remote_name;

DROP INDEX transactions_id;
CREATE UNIQUE INDEX transactions_id ON transactions(id);
DROP INDEX operations_id;
CREATE UNIQUE INDEX operations_id ON operations(id);
COMMIT;
3 changes: 3 additions & 0 deletions db/migrations/postgres/000095_change_namespace_fields.up.sql
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,7 @@ ALTER TABLE namespaces DROP COLUMN ntype;
ALTER TABLE namespaces ADD COLUMN remote_name VARCHAR(64);
UPDATE namespaces SET remote_name = name;
ALTER TABLE namespaces ALTER COLUMN remote_name SET NOT NULL;

DROP INDEX transactions_id;
CREATE UNIQUE INDEX transactions_id ON transactions(namespace, id);
COMMIT;
5 changes: 5 additions & 0 deletions db/migrations/sqlite/000095_change_namespace_fields.down.sql
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,8 @@ ALTER TABLE namespaces ADD COLUMN id UUID;
ALTER TABLE namespaces ADD COLUMN message_id UUID;
ALTER TABLE namespaces ADD COLUMN ntype VARCHAR(64);
ALTER TABLE namespaces DROP COLUMN remote_name;

DROP INDEX transactions_id;
CREATE UNIQUE INDEX transactions_id ON transactions(id);
DROP INDEX operations_id;
CREATE UNIQUE INDEX operations_id ON operations(id);
3 changes: 3 additions & 0 deletions db/migrations/sqlite/000095_change_namespace_fields.up.sql
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@ ALTER TABLE namespaces DROP COLUMN message_id;
ALTER TABLE namespaces DROP COLUMN ntype;
ALTER TABLE namespaces ADD COLUMN remote_name VARCHAR(64);
UPDATE namespaces SET remote_name = name;

DROP INDEX transactions_id;
CREATE UNIQUE INDEX transactions_id ON transactions(namespace, id);
14 changes: 14 additions & 0 deletions docs/swagger/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20467,6 +20467,9 @@ paths:
description: The backend identifier of the subscription
for the FireFly BatchPin contract
type: string
version:
description: The version of this multiparty contract
type: integer
type: object
location:
description: A blockchain specific contract identifier.
Expand Down Expand Up @@ -20500,6 +20503,10 @@ paths:
description: The backend identifier of the subscription
for the FireFly BatchPin contract
type: string
version:
description: The version of this multiparty
contract
type: integer
type: object
location:
description: A blockchain specific contract identifier.
Expand Down Expand Up @@ -28021,6 +28028,9 @@ paths:
description: The backend identifier of the subscription
for the FireFly BatchPin contract
type: string
version:
description: The version of this multiparty contract
type: integer
type: object
location:
description: A blockchain specific contract identifier.
Expand Down Expand Up @@ -28054,6 +28064,10 @@ paths:
description: The backend identifier of the subscription
for the FireFly BatchPin contract
type: string
version:
description: The version of this multiparty
contract
type: integer
type: object
location:
description: A blockchain specific contract identifier.
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ require (
gitlab.com/hfuss/mux-prometheus v0.0.4
golang.org/x/net v0.0.0-20220531201128-c960675eff93
golang.org/x/text v0.3.7
gopkg.in/yaml.v2 v2.4.0
)

require (
Expand Down Expand Up @@ -81,6 +82,5 @@ require (
google.golang.org/protobuf v1.28.0 // indirect
gopkg.in/ini.v1 v1.66.6 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
8 changes: 5 additions & 3 deletions internal/apiserver/route_get_namespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,11 @@ var getNamespace = &ffapi.Route{
JSONOutputCodes: []int{http.StatusOK},
Extensions: &coreExtensions{
CoreJSONHandler: func(r *ffapi.APIRequest, cr *coreRequest) (output interface{}, err error) {
ns := r.PP["ns"]
or := cr.mgr.Orchestrator(ns)
return or.GetNamespace(cr.ctx), nil
or, err := getOrchestrator(cr.ctx, cr.mgr, routeTagNonDefaultNamespace, r)
if err == nil {
output = or.GetNamespace(cr.ctx)
}
return output, err
},
},
}
15 changes: 15 additions & 0 deletions internal/apiserver/route_get_namespace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package apiserver

import (
"context"
"net/http/httptest"
"testing"

Expand All @@ -38,3 +39,17 @@ func TestGetNamespace(t *testing.T) {

assert.Equal(t, 200, res.Result().StatusCode)
}

func TestGetNamespaceInvalid(t *testing.T) {
mgr, o, as := newTestServer()
r := as.createMuxRouter(context.Background(), mgr)
o.On("Authorize", mock.Anything, mock.Anything).Return(nil)
req := httptest.NewRequest("GET", "/api/v1/namespaces/BAD", nil)
req.Header.Set("Content-Type", "application/json; charset=utf-8")
res := httptest.NewRecorder()

mgr.On("Orchestrator", "BAD").Return(nil, nil)
r.ServeHTTP(res, req)

assert.Equal(t, 404, res.Result().StatusCode)
}
8 changes: 5 additions & 3 deletions internal/apiserver/route_spi_get_namespace_by_name.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,11 @@ var spiGetNamespaceByName = &ffapi.Route{
JSONOutputCodes: []int{http.StatusOK},
Extensions: &coreExtensions{
CoreJSONHandler: func(r *ffapi.APIRequest, cr *coreRequest) (output interface{}, err error) {
ns := r.PP["ns"]
or := cr.mgr.Orchestrator(ns)
return or.GetNamespace(cr.ctx), nil
or, err := getOrchestrator(cr.ctx, cr.mgr, routeTagNonDefaultNamespace, r)
if err == nil {
output = or.GetNamespace(cr.ctx)
}
return output, err
},
},
}
43 changes: 43 additions & 0 deletions internal/apiserver/route_spi_post_reset.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright © 2022 Kaleido, Inc.
//
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package apiserver

import (
"net/http"

"github.com/hyperledger/firefly-common/pkg/ffapi"
"github.com/hyperledger/firefly/internal/coremsgs"
)

var spiPostReset = &ffapi.Route{
Name: "spiPostReset",
Path: "reset",
Method: http.MethodPost,
PathParams: nil,
QueryParams: nil,
Description: coremsgs.APIEndpointsAdminPostReset,
JSONInputValue: nil,
JSONOutputValue: nil,
JSONOutputCodes: []int{http.StatusNoContent},
Extensions: &coreExtensions{
FilterFactory: nil,
CoreJSONHandler: func(r *ffapi.APIRequest, cr *coreRequest) (output interface{}, err error) {
cr.mgr.Reset(cr.ctx)
return nil, nil
},
},
}
39 changes: 39 additions & 0 deletions internal/apiserver/route_spi_post_reset_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright © 2021 Kaleido, Inc.
//
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package apiserver

import (
"bytes"
"net/http/httptest"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)

func TestAdminPostResetConfig(t *testing.T) {
mgr, _, as := newTestServer()
r := as.createAdminMuxRouter(mgr)
req := httptest.NewRequest("POST", "/spi/v1/reset", bytes.NewReader([]byte(`{}`)))
req.Header.Set("Content-Type", "application/json; charset=utf-8")
res := httptest.NewRecorder()

mgr.On("Reset", mock.Anything).Return()
r.ServeHTTP(res, req)

assert.Equal(t, 204, res.Result().StatusCode)
}
1 change: 1 addition & 0 deletions internal/apiserver/spi_routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@ var spiRoutes = []*ffapi.Route{
spiGetOpByID,
spiGetOps,
spiPatchOpByID,
spiPostReset,
}
Loading

0 comments on commit f0a2f26

Please sign in to comment.