Repository: hyperledger/firefly
Pull request #826 (2022-05-19 21:02:51)

Factor out Namespace Manager
Part of https://github.com/hyperledger/firefly-fir/pull/12

~~In a chain with #821~~

This just creates a new Namespace Manager component to factor namespace-related code out of Orchestrator. Going forward, this component will take more responsibility for determining which plugins to use for each namespace, and other components will consult it dynamically instead of retaining their own list of plugins.

codecov-commenter: # [Codecov](https://codecov.io/gh/hyperledger/firefly/pull/826?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger) Report
> Merging [#826](https://codecov.io/gh/hyperledger/firefly/pull/826?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger) (a50a24a) into [main](https://codecov.io/gh/hyperledger/firefly/commit/1d45d861658b3f1b3941da65cad8240280e30827?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger) (1d45d86) will **increase** coverage by `0.01%`.
> The diff coverage is `99.77%`.

```diff
@@            Coverage Diff             @@
##             main     #826      +/-   ##
==========================================
+ Coverage   99.97%   99.99%   +0.01%     
==========================================
  Files         304      299       -5     
  Lines       18958    19120     +162     
==========================================
+ Hits        18954    19119     +165     
+ Misses          4        1       -3     
```


| [Impacted Files](https://codecov.io/gh/hyperledger/firefly/pull/826?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger) | Coverage Δ | |
|---|---|---|
| [internal/coreconfig/coreconfig.go](https://codecov.io/gh/hyperledger/firefly/pull/826/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger#diff-aW50ZXJuYWwvY29yZWNvbmZpZy9jb3JlY29uZmlnLmdv) | `100.00% <ø> (ø)` | |
| [internal/orchestrator/orchestrator.go](https://codecov.io/gh/hyperledger/firefly/pull/826/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger#diff-aW50ZXJuYWwvb3JjaGVzdHJhdG9yL29yY2hlc3RyYXRvci5nbw==) | `99.77% <99.66%> (-0.23%)` | :arrow_down: |
| [internal/apiserver/server.go](https://codecov.io/gh/hyperledger/firefly/pull/826/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger#diff-aW50ZXJuYWwvYXBpc2VydmVyL3NlcnZlci5nbw==) | `100.00% <100.00%> (ø)` | |
| [internal/database/sqlcommon/sqlcommon.go](https://codecov.io/gh/hyperledger/firefly/pull/826/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger#diff-aW50ZXJuYWwvZGF0YWJhc2Uvc3FsY29tbW9uL3NxbGNvbW1vbi5nbw==) | `100.00% <100.00%> (ø)` | |
| [internal/identity/identitymanager.go](https://codecov.io/gh/hyperledger/firefly/pull/826/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger#diff-aW50ZXJuYWwvaWRlbnRpdHkvaWRlbnRpdHltYW5hZ2VyLmdv) | `100.00% <100.00%> (ø)` | |
| [internal/namespace/config.go](https://codecov.io/gh/hyperledger/firefly/pull/826/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger#diff-aW50ZXJuYWwvbmFtZXNwYWNlL2NvbmZpZy5nbw==) | `100.00% <100.00%> (ø)` | |
| [internal/namespace/manager.go](https://codecov.io/gh/hyperledger/firefly/pull/826/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger#diff-aW50ZXJuYWwvbmFtZXNwYWNlL21hbmFnZXIuZ28=) | `100.00% <100.00%> (ø)` | |
| [internal/orchestrator/chart.go](https://codecov.io/gh/hyperledger/firefly/pull/826/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger#diff-aW50ZXJuYWwvb3JjaGVzdHJhdG9yL2NoYXJ0Lmdv) | `100.00% <100.00%> (ø)` | |
| [internal/orchestrator/data\_query.go](https://codecov.io/gh/hyperledger/firefly/pull/826/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger#diff-aW50ZXJuYWwvb3JjaGVzdHJhdG9yL2RhdGFfcXVlcnkuZ28=) | `100.00% <100.00%> (ø)` | |
| [internal/orchestrator/status.go](https://codecov.io/gh/hyperledger/firefly/pull/826/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger#diff-aW50ZXJuYWwvb3JjaGVzdHJhdG9yL3N0YXR1cy5nbw==) | `100.00% <100.00%> (ø)` | |
| ... and [5 more](https://codecov.io/gh/hyperledger/firefly/pull/826/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/hyperledger/firefly/pull/826?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/hyperledger/firefly/pull/826?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger). Last update [1d45d86...a50a24a](https://codecov.io/gh/hyperledger/firefly/pull/826?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger).


Patch:
diff --git a/Makefile b/Makefile
index 6dc1c2966..63b93b717 100644
--- a/Makefile
+++ b/Makefile
@@ -65,6 +65,7 @@ $(eval $(call makemock, internal/shareddownload,   Manager,            shareddow
 $(eval $(call makemock, internal/shareddownload,   Callbacks,          shareddownloadmocks))
 $(eval $(call makemock, internal/definitions,      DefinitionHandler,  definitionsmocks))
 $(eval $(call makemock, internal/events,           EventManager,       eventmocks))
+$(eval $(call makemock, internal/namespace,        Manager,            namespacemocks))
 $(eval $(call makemock, internal/networkmap,       Manager,            networkmapmocks))
 $(eval $(call makemock, internal/assets,           Manager,            assetmocks))
 $(eval $(call makemock, internal/contracts,        Manager,            contractmocks))
diff --git a/internal/coreconfig/coreconfig.go b/internal/coreconfig/coreconfig.go
index d9df62bec..382c13f45 100644
--- a/internal/coreconfig/coreconfig.go
+++ b/internal/coreconfig/coreconfig.go
@@ -29,6 +29,10 @@ const (
 	PluginConfigName = "name"
 	// PluginConfigType is the type of the plugin to be loaded
 	PluginConfigType = "type"
+	// NamespaceName is a short name for a pre-defined namespace
+	NamespaceName = "name"
+	// NamespaceName is a long description for a pre-defined namespace
+	NamespaceDescription = "description"
 )
 
 // The following keys can be access from the root configuration.
diff --git a/internal/namespace/config.go b/internal/namespace/config.go
new file mode 100644
index 000000000..52c0cc450
--- /dev/null
+++ b/internal/namespace/config.go
@@ -0,0 +1,41 @@
+// 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 namespace
+
+import (
+	"github.com/hyperledger/firefly-common/pkg/config"
+	"github.com/hyperledger/firefly/internal/coreconfig"
+)
+
+const (
+	// NamespacePredefined is the list of pre-defined namespaces
+	NamespacePredefined = "predefined"
+)
+
+var (
+	namespaceConfig     = config.RootSection("namespaces")
+	namespacePredefined = namespaceConfig.SubArray(NamespacePredefined)
+)
+
+func InitConfig(withDefaults bool) {
+	namespacePredefined.AddKnownKey(coreconfig.NamespaceName)
+	namespacePredefined.AddKnownKey(coreconfig.NamespaceDescription)
+	if withDefaults {
+		namespaceConfig.AddKnownKey(NamespacePredefined+".0."+coreconfig.NamespaceName, "default")
+		namespaceConfig.AddKnownKey(NamespacePredefined+".0."+coreconfig.NamespaceDescription, "Default predefined namespace")
+	}
+}
diff --git a/internal/namespace/manager.go b/internal/namespace/manager.go
new file mode 100644
index 000000000..c7981bc8b
--- /dev/null
+++ b/internal/namespace/manager.go
@@ -0,0 +1,126 @@
+// 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 namespace
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/hyperledger/firefly-common/pkg/config"
+	"github.com/hyperledger/firefly-common/pkg/fftypes"
+	"github.com/hyperledger/firefly-common/pkg/i18n"
+	"github.com/hyperledger/firefly-common/pkg/log"
+	"github.com/hyperledger/firefly/internal/coreconfig"
+	"github.com/hyperledger/firefly/internal/coremsgs"
+	"github.com/hyperledger/firefly/pkg/core"
+	"github.com/hyperledger/firefly/pkg/database"
+)
+
+type Manager interface {
+	// Init initializes the manager
+	Init(ctx context.Context, di database.Plugin) error
+}
+
+type namespaceManager struct {
+	ctx      context.Context
+	nsConfig map[string]config.Section
+}
+
+func NewNamespaceManager(ctx context.Context) Manager {
+	nm := &namespaceManager{
+		ctx:      ctx,
+		nsConfig: buildNamespaceMap(ctx),
+	}
+	return nm
+}
+
+func buildNamespaceMap(ctx context.Context) map[string]config.Section {
+	conf := namespacePredefined
+	namespaces := make(map[string]config.Section, conf.ArraySize())
+	for i := 0; i < conf.ArraySize(); i++ {
+		nsConfig := conf.ArrayEntry(i)
+		name := nsConfig.GetString(coreconfig.NamespaceName)
+		if name != "" {
+			if _, ok := namespaces[name]; ok {
+				log.L(ctx).Warnf("Duplicate predefined namespace (ignored): %s", name)
+			}
+			namespaces[name] = nsConfig
+		}
+	}
+	return namespaces
+}
+
+func (nm *namespaceManager) Init(ctx context.Context, di database.Plugin) error {
+	return nm.initNamespaces(ctx, di)
+}
+
+func (nm *namespaceManager) getPredefinedNamespaces(ctx context.Context) ([]*core.Namespace, error) {
+	defaultNS := config.GetString(coreconfig.NamespacesDefault)
+	namespaces := []*core.Namespace{
+		{
+			Name:        core.SystemNamespace,
+			Type:        core.NamespaceTypeSystem,
+			Description: i18n.Expand(ctx, coremsgs.CoreSystemNSDescription),
+		},
+	}
+	i := 0
+	foundDefault := false
+	for name, nsObject := range nm.nsConfig {
+		if err := core.ValidateFFNameField(ctx, name, fmt.Sprintf("namespaces.predefined[%d].name", i)); err != nil {
+			return nil, err
+		}
+		i++
+		foundDefault = foundDefault || name == defaultNS
+		namespaces = append(namespaces, &core.Namespace{
+			Type:        core.NamespaceTypeLocal,
+			Name:        name,
+			Description: nsObject.GetString("description"),
+		})
+	}
+	if !foundDefault {
+		return nil, i18n.NewError(ctx, coremsgs.MsgDefaultNamespaceNotFound, defaultNS)
+	}
+	return namespaces, nil
+}
+
+func (nm *namespaceManager) initNamespaces(ctx context.Context, di database.Plugin) error {
+	predefined, err := nm.getPredefinedNamespaces(ctx)
+	if err != nil {
+		return err
+	}
+	for _, newNS := range predefined {
+		ns, err := di.GetNamespace(ctx, newNS.Name)
+		if err != nil {
+			return err
+		}
+		var updated bool
+		if ns == nil {
+			updated = true
+			newNS.ID = fftypes.NewUUID()
+			newNS.Created = fftypes.Now()
+		} else {
+			// Only update if the description has changed, and the one in our DB is locally defined
+			updated = ns.Description != newNS.Description && ns.Type == core.NamespaceTypeLocal
+		}
+		if updated {
+			if err := di.UpsertNamespace(ctx, newNS, true); err != nil {
+				return err
+			}
+		}
+	}
+	return nil
+}
diff --git a/internal/orchestrator/orchestrator.go b/internal/orchestrator/orchestrator.go
index 219cbbdb4..be19fc3a8 100644
--- a/internal/orchestrator/orchestrator.go
+++ b/internal/orchestrator/orchestrator.go
@@ -18,7 +18,6 @@ package orchestrator
 
 import (
 	"context"
-	"fmt"
 
 	"github.com/hyperledger/firefly-common/pkg/config"
 	"github.com/hyperledger/firefly-common/pkg/fftypes"
@@ -41,6 +40,7 @@ import (
 	"github.com/hyperledger/firefly/internal/identity"
 	"github.com/hyperledger/firefly/internal/identity/iifactory"
 	"github.com/hyperledger/firefly/internal/metrics"
+	"github.com/hyperledger/firefly/internal/namespace"
 	"github.com/hyperledger/firefly/internal/networkmap"
 	"github.com/hyperledger/firefly/internal/operations"
 	"github.com/hyperledger/firefly/internal/privatemessaging"
@@ -58,19 +58,9 @@ import (
 	"github.com/hyperledger/firefly/pkg/tokens"
 )
 
-const (
-	// NamespacePredefined is the list of pre-defined namespaces
-	NamespacePredefined = "predefined"
-	// NamespaceName is a short name for a pre-defined namespace
-	NamespaceName = "name"
-	// NamespaceName is a long description for a pre-defined namespace
-	NamespaceDescription = "description"
-)
-
 var (
 	blockchainConfig    = config.RootArray("plugins.blockchain")
 	tokensConfig        = config.RootArray("plugins.tokens")
-	namespaceConfig     = config.RootSection("namespaces")
 	databaseConfig      = config.RootArray("plugins.database")
 	sharedstorageConfig = config.RootArray("plugins.sharedstorage")
 	dataexchangeConfig  = config.RootArray("plugins.dataexchange")
@@ -185,7 +175,7 @@ type orchestrator struct {
 	adminEvents          adminevents.Manager
 	sharedDownload       shareddownload.Manager
 	txHelper             txcommon.Helper
-	predefinedNS         config.ArraySection
+	namespace            namespace.Manager
 }
 
 func NewOrchestrator(withDefaults bool) Orchestrator {
@@ -201,34 +191,22 @@ func NewOrchestrator(withDefaults bool) Orchestrator {
 	dxfactory.InitConfig(dataexchangeConfig)
 	dxfactory.InitConfigDeprecated(deprecatedDataexchangeConfig)
 	iifactory.InitConfig(identityConfig)
-	// For backwards compatibility with the top level "tokens" config
 	tifactory.InitConfigDeprecated(deprecatedTokensConfig)
 	tifactory.InitConfig(tokensConfig)
-
-	or.InitNamespaceConfig(withDefaults)
+	namespace.InitConfig(withDefaults)
 
 	return or
 }
 
-func (or *orchestrator) InitNamespaceConfig(withDefaults bool) {
-	or.predefinedNS = namespaceConfig.SubArray(NamespacePredefined)
-	or.predefinedNS.AddKnownKey(NamespaceName)
-	or.predefinedNS.AddKnownKey(NamespaceDescription)
-	if withDefaults {
-		namespaceConfig.AddKnownKey(NamespacePredefined+".0."+NamespaceName, "default")
-		namespaceConfig.AddKnownKey(NamespacePredefined+".0."+NamespaceDescription, "Default predefined namespace")
-	}
-}
-
 func (or *orchestrator) Init(ctx context.Context, cancelCtx context.CancelFunc) (err error) {
 	or.ctx = ctx
 	or.cancelCtx = cancelCtx
 	err = or.initPlugins(ctx)
 	if err == nil {
-		err = or.initComponents(ctx)
+		err = or.initNamespaces(ctx)
 	}
 	if err == nil {
-		err = or.initNamespaces(ctx)
+		err = or.initComponents(ctx)
 	}
 	// Bind together the blockchain interface callbacks, with the events manager
 	or.bc.bi = or.blockchain
@@ -495,6 +473,12 @@ func (or *orchestrator) initPlugins(ctx context.Context) (err error) {
 		}
 	}
 
+	// Not really a plugin, but this has to be initialized here after the database (at least temporarily).
+	// Shortly after this step, namespaces will be synced to the database and will generate notifications to adminEvents.
+	if or.adminEvents == nil {
+		or.adminEvents = adminevents.NewAdminEventManager(ctx)
+	}
+
 	if or.identityPlugins == nil {
 		if err = or.initIdentity(ctx); err != nil {
 			return err
@@ -864,10 +848,6 @@ func (or *orchestrator) initComponents(ctx context.Context) (err error) {
 		}
 	}
 
-	if or.adminEvents == nil {
-		or.adminEvents = adminevents.NewAdminEventManager(ctx)
-	}
-
 	if or.networkmap == nil {
 		or.networkmap, err = networkmap.NewNetworkMap(ctx, or.database, or.broadcast, or.dataexchange, or.identity, or.syncasync)
 		if err != nil {
@@ -880,70 +860,9 @@ func (or *orchestrator) initComponents(ctx context.Context) (err error) {
 	return nil
 }
 
-func (or *orchestrator) getPredefinedNamespaces(ctx context.Context) ([]*core.Namespace, error) {
-	defaultNS := config.GetString(coreconfig.NamespacesDefault)
-	namespaces := []*core.Namespace{
-		{
-			Name:        core.SystemNamespace,
-			Type:        core.NamespaceTypeSystem,
-			Description: i18n.Expand(ctx, coremsgs.CoreSystemNSDescription),
-		},
-	}
-	foundDefault := false
-	for i := 0; i < or.predefinedNS.ArraySize(); i++ {
-		nsObject := or.predefinedNS.ArrayEntry(i)
-		name := nsObject.GetString("name")
-		err := core.ValidateFFNameField(ctx, name, fmt.Sprintf("namespaces.predefined[%d].name", i))
-		if err != nil {
-			return nil, err
-		}
-		foundDefault = foundDefault || name == defaultNS
-		description := nsObject.GetString("description")
-		dup := false
-		for _, existing := range namespaces {
-			if existing.Name == name {
-				log.L(ctx).Warnf("Duplicate predefined namespace (ignored): %s", name)
-				dup = true
-			}
-		}
-		if !dup {
-			namespaces = append(namespaces, &core.Namespace{
-				Type:        core.NamespaceTypeLocal,
-				Name:        name,
-				Description: description,
-			})
-		}
-	}
-	if !foundDefault {
-		return nil, i18n.NewError(ctx, coremsgs.MsgDefaultNamespaceNotFound, defaultNS)
-	}
-	return namespaces, nil
-}
-
-func (or *orchestrator) initNamespaces(ctx context.Context) error {
-	predefined, err := or.getPredefinedNamespaces(ctx)
-	if err != nil {
-		return err
-	}
-	for _, newNS := range predefined {
-		ns, err := or.database.GetNamespace(ctx, newNS.Name)
-		if err != nil {
-			return err
-		}
-		var updated bool
-		if ns == nil {
-			updated = true
-			newNS.ID = fftypes.NewUUID()
-			newNS.Created = fftypes.Now()
-		} else {
-			// Only update if the description has changed, and the one in our DB is locally defined
-			updated = ns.Description != newNS.Description && ns.Type == core.NamespaceTypeLocal
-		}
-		if updated {
-			if err := or.database.UpsertNamespace(ctx, newNS, true); err != nil {
-				return err
-			}
-		}
+func (or *orchestrator) initNamespaces(ctx context.Context) (err error) {
+	if or.namespace == nil {
+		or.namespace = namespace.NewNamespaceManager(ctx)
 	}
-	return nil
+	return or.namespace.Init(ctx, or.database)
 }
diff --git a/mocks/namespacemocks/manager.go b/mocks/namespacemocks/manager.go
new file mode 100644
index 000000000..cec55ed53
--- /dev/null
+++ b/mocks/namespacemocks/manager.go
@@ -0,0 +1,29 @@
+// Code generated by mockery v1.0.0. DO NOT EDIT.
+
+package namespacemocks
+
+import (
+	context "context"
+
+	database "github.com/hyperledger/firefly/pkg/database"
+	mock "github.com/stretchr/testify/mock"
+)
+
+// Manager is an autogenerated mock type for the Manager type
+type Manager struct {
+	mock.Mock
+}
+
+// Init provides a mock function with given fields: ctx, di
+func (_m *Manager) Init(ctx context.Context, di database.Plugin) error {
+	ret := _m.Called(ctx, di)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(context.Context, database.Plugin) error); ok {
+		r0 = rf(ctx, di)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}


Test patch:
diff --git a/internal/namespace/manager_test.go b/internal/namespace/manager_test.go
new file mode 100644
index 000000000..d3fd776f1
--- /dev/null
+++ b/internal/namespace/manager_test.go
@@ -0,0 +1,143 @@
+// 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 namespace
+
+import (
+	"context"
+	"fmt"
+	"testing"
+
+	"github.com/hyperledger/firefly-common/pkg/config"
+	"github.com/hyperledger/firefly-common/pkg/fftypes"
+	"github.com/hyperledger/firefly/internal/coreconfig"
+	"github.com/hyperledger/firefly/mocks/databasemocks"
+	"github.com/hyperledger/firefly/pkg/core"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
+)
+
+type testNamespaceManager struct {
+	namespaceManager
+	mdi *databasemocks.Plugin
+}
+
+func (nm *testNamespaceManager) cleanup(t *testing.T) {
+	nm.mdi.AssertExpectations(t)
+}
+
+func newTestNamespaceManager(resetConfig bool) *testNamespaceManager {
+	if resetConfig {
+		coreconfig.Reset()
+		InitConfig(true)
+	}
+	nm := &testNamespaceManager{
+		mdi: &databasemocks.Plugin{},
+		namespaceManager: namespaceManager{
+			nsConfig: buildNamespaceMap(context.Background()),
+		},
+	}
+	return nm
+}
+
+func TestNewNamespaceManager(t *testing.T) {
+	nm := NewNamespaceManager(context.Background())
+	assert.NotNil(t, nm)
+}
+
+func TestInit(t *testing.T) {
+	coreconfig.Reset()
+	nm := newTestNamespaceManager(false)
+	defer nm.cleanup(t)
+
+	nm.Init(context.Background(), nm.mdi)
+}
+
+func TestInitNamespacesBadName(t *testing.T) {
+	coreconfig.Reset()
+	namespaceConfig.AddKnownKey("predefined.0."+coreconfig.NamespaceName, "!Badness")
+
+	nm := newTestNamespaceManager(false)
+	defer nm.cleanup(t)
+
+	err := nm.initNamespaces(context.Background(), nm.mdi)
+	assert.Regexp(t, "FF00140", err)
+}
+
+func TestInitNamespacesGetFail(t *testing.T) {
+	nm := newTestNamespaceManager(true)
+	defer nm.cleanup(t)
+
+	nm.mdi.On("GetNamespace", mock.Anything, mock.Anything).Return(nil, fmt.Errorf("pop"))
+	err := nm.initNamespaces(context.Background(), nm.mdi)
+	assert.Regexp(t, "pop", err)
+}
+
+func TestInitNamespacesUpsertFail(t *testing.T) {
+	nm := newTestNamespaceManager(true)
+	defer nm.cleanup(t)
+
+	nm.mdi.On("GetNamespace", mock.Anything, mock.Anything).Return(nil, nil)
+	nm.mdi.On("UpsertNamespace", mock.Anything, mock.Anything, true).Return(fmt.Errorf("pop"))
+	err := nm.initNamespaces(context.Background(), nm.mdi)
+	assert.Regexp(t, "pop", err)
+}
+
+func TestInitNamespacesUpsertNotNeeded(t *testing.T) {
+	nm := newTestNamespaceManager(true)
+	defer nm.cleanup(t)
+
+	nm.mdi.On("GetNamespace", mock.Anything, mock.Anything).Return(&core.Namespace{
+		Type: core.NamespaceTypeBroadcast, // any broadcasted NS will not be updated
+	}, nil)
+	err := nm.initNamespaces(context.Background(), nm.mdi)
+	assert.NoError(t, err)
+}
+
+func TestInitNamespacesDefaultMissing(t *testing.T) {
+	coreconfig.Reset()
+	config.Set(coreconfig.NamespacesPredefined, fftypes.JSONObjectArray{})
+
+	nm := newTestNamespaceManager(false)
+	defer nm.cleanup(t)
+
+	err := nm.initNamespaces(context.Background(), nm.mdi)
+	assert.Regexp(t, "FF10166", err)
+}
+
+func TestInitNamespacesDupName(t *testing.T) {
+	coreconfig.Reset()
+	InitConfig(true)
+
+	namespaceConfig.AddKnownKey("predefined.0.name", "ns1")
+	namespaceConfig.AddKnownKey("predefined.1.name", "ns2")
+	namespaceConfig.AddKnownKey("predefined.2.name", "ns2")
+	config.Set(coreconfig.NamespacesDefault, "ns1")
+
+	nm := newTestNamespaceManager(false)
+	defer nm.cleanup(t)
+
+	nsList, err := nm.getPredefinedNamespaces(context.Background())
+	assert.NoError(t, err)
+	assert.Len(t, nsList, 3)
+	names := make([]string, len(nsList))
+	for i, ns := range nsList {
+		names[i] = ns.Name
+	}
+	assert.Contains(t, names, core.SystemNamespace)
+	assert.Contains(t, names, "ns1")
+	assert.Contains(t, names, "ns2")
+}
diff --git a/internal/orchestrator/orchestrator_test.go b/internal/orchestrator/orchestrator_test.go
index 288aec9f2..a4a2a5bdf 100644
--- a/internal/orchestrator/orchestrator_test.go
+++ b/internal/orchestrator/orchestrator_test.go
@@ -46,6 +46,7 @@ import (
 	"github.com/hyperledger/firefly/mocks/identitymanagermocks"
 	"github.com/hyperledger/firefly/mocks/identitymocks"
 	"github.com/hyperledger/firefly/mocks/metricsmocks"
+	"github.com/hyperledger/firefly/mocks/namespacemocks"
 	"github.com/hyperledger/firefly/mocks/networkmapmocks"
 	"github.com/hyperledger/firefly/mocks/operationmocks"
 	"github.com/hyperledger/firefly/mocks/privatemessagingmocks"
@@ -91,6 +92,33 @@ type testOrchestrator struct {
 	msd *shareddownloadmocks.Manager
 	mae *admineventsmocks.Manager
 	mdh *definitionsmocks.DefinitionHandler
+	mns *namespacemocks.Manager
+}
+
+func (tor *testOrchestrator) cleanup(t *testing.T) {
+	tor.mdi.AssertExpectations(t)
+	tor.mdm.AssertExpectations(t)
+	tor.mbm.AssertExpectations(t)
+	tor.mba.AssertExpectations(t)
+	tor.mem.AssertExpectations(t)
+	tor.mnm.AssertExpectations(t)
+	tor.mps.AssertExpectations(t)
+	tor.mpm.AssertExpectations(t)
+	tor.mbi.AssertExpectations(t)
+	tor.mii.AssertExpectations(t)
+	tor.mim.AssertExpectations(t)
+	tor.mdx.AssertExpectations(t)
+	tor.mam.AssertExpectations(t)
+	tor.mti.AssertExpectations(t)
+	tor.mcm.AssertExpectations(t)
+	tor.mmi.AssertExpectations(t)
+	tor.mom.AssertExpectations(t)
+	tor.mbp.AssertExpectations(t)
+	tor.mth.AssertExpectations(t)
+	tor.msd.AssertExpectations(t)
+	tor.mae.AssertExpectations(t)
+	tor.mdh.AssertExpectations(t)
+	tor.mns.AssertExpectations(t)
 }
 
 func newTestOrchestrator() *testOrchestrator {
@@ -123,6 +151,7 @@ func newTestOrchestrator() *testOrchestrator {
 		msd: &shareddownloadmocks.Manager{},
 		mae: &admineventsmocks.Manager{},
 		mdh: &definitionsmocks.DefinitionHandler{},
+		mns: &namespacemocks.Manager{},
 	}
 	tor.orchestrator.databases = map[string]database.Plugin{"postgres": tor.mdi}
 	tor.orchestrator.data = tor.mdm
@@ -146,6 +175,7 @@ func newTestOrchestrator() *testOrchestrator {
 	tor.orchestrator.adminEvents = tor.mae
 	tor.orchestrator.txHelper = tor.mth
 	tor.orchestrator.definitions = tor.mdh
+	tor.orchestrator.namespace = tor.mns
 	tor.mdi.On("Name").Return("mock-di").Maybe()
 	tor.mem.On("Name").Return("mock-ei").Maybe()
 	tor.mps.On("Name").Return("mock-ps").Maybe()
@@ -156,7 +186,6 @@ func newTestOrchestrator() *testOrchestrator {
 	tor.mti.On("Name").Return("mock-tk").Maybe()
 	tor.mcm.On("Name").Return("mock-cm").Maybe()
 	tor.mmi.On("Name").Return("mock-mm").Maybe()
-	tor.orchestrator.InitNamespaceConfig(true)
 	return tor
 }
 
@@ -167,6 +196,7 @@ func TestNewOrchestrator(t *testing.T) {
 
 func TestBadDeprecatedDatabasePlugin(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	difactory.InitConfigDeprecated(deprecatedDatabaseConfig)
 	deprecatedDatabaseConfig.Set(coreconfig.PluginConfigType, "wrong")
 	or.databases = nil
@@ -177,6 +207,7 @@ func TestBadDeprecatedDatabasePlugin(t *testing.T) {
 
 func TestBadDeprecatedDatabaseInitFail(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	difactory.InitConfigDeprecated(deprecatedDatabaseConfig)
 	deprecatedDatabaseConfig.AddKnownKey(coreconfig.PluginConfigType, "test")
 	or.mdi.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("pop"))
@@ -187,6 +218,7 @@ func TestBadDeprecatedDatabaseInitFail(t *testing.T) {
 
 func TestDatabaseGetPlugins(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	difactory.InitConfig(databaseConfig)
 	config.Set("plugins.database", []fftypes.JSONObject{{}})
 	databaseConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip")
@@ -199,6 +231,7 @@ func TestDatabaseGetPlugins(t *testing.T) {
 
 func TestDatabaseUnknownPlugin(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	difactory.InitConfig(databaseConfig)
 	config.Set("plugins.database", []fftypes.JSONObject{{}})
 	databaseConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip")
@@ -211,6 +244,7 @@ func TestDatabaseUnknownPlugin(t *testing.T) {
 
 func TestDatabaseGetPluginsNoName(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	difactory.InitConfig(databaseConfig)
 	config.Set("plugins.database", []fftypes.JSONObject{{}})
 	databaseConfig.AddKnownKey(coreconfig.PluginConfigType, "postgres")
@@ -222,12 +256,12 @@ func TestDatabaseGetPluginsNoName(t *testing.T) {
 
 func TestDatabaseGetPluginsBadName(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	or.databases = nil
 	difactory.InitConfig(databaseConfig)
 	config.Set("plugins.database", []fftypes.JSONObject{{}})
 	databaseConfig.AddKnownKey(coreconfig.PluginConfigName, "wrong////")
 	databaseConfig.AddKnownKey(coreconfig.PluginConfigType, "postgres")
-	or.mii.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
 	ctx := context.Background()
 	err := or.initPlugins(ctx)
 	assert.Error(t, err)
@@ -235,6 +269,7 @@ func TestDatabaseGetPluginsBadName(t *testing.T) {
 
 func TestDeprecatedDatabaseInitPlugin(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	difactory.InitConfigDeprecated(deprecatedDatabaseConfig)
 	deprecatedDatabaseConfig.AddKnownKey(coreconfig.PluginConfigType, "postgres")
 	or.mdi.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
@@ -245,6 +280,7 @@ func TestDeprecatedDatabaseInitPlugin(t *testing.T) {
 
 func TestDatabaseInitPlugins(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	difactory.InitConfig(databaseConfig)
 	config.Set("plugins.database", []fftypes.JSONObject{{}})
 	databaseConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip")
@@ -260,9 +296,9 @@ func TestDatabaseInitPlugins(t *testing.T) {
 
 func TestDatabaseInitPluginFail(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	or.databases = nil
 	difactory.InitConfig(databaseConfig)
-	or.mii.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
 	config.Set("plugins.database", []fftypes.JSONObject{{}})
 	databaseConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip")
 	databaseConfig.AddKnownKey(coreconfig.PluginConfigType, "sqlite3")
@@ -273,9 +309,9 @@ func TestDatabaseInitPluginFail(t *testing.T) {
 
 func TestDeprecatedDatabaseInitPluginFail(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	or.databases = nil
 	difactory.InitConfigDeprecated(deprecatedDatabaseConfig)
-	or.mii.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
 	deprecatedDatabaseConfig.AddKnownKey(coreconfig.PluginConfigType, "sqlite3")
 	ctx := context.Background()
 	err := or.initPlugins(ctx)
@@ -284,12 +320,12 @@ func TestDeprecatedDatabaseInitPluginFail(t *testing.T) {
 
 func TestIdentityPluginMissingType(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	or.database = or.mdi
 	or.identityPlugins = nil
 	iifactory.InitConfig(identityConfig)
 	identityConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip")
 	config.Set("plugins.identity", []fftypes.JSONObject{{}})
-	or.mdi.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
 	ctx, cancelCtx := context.WithCancel(context.Background())
 	err := or.Init(ctx, cancelCtx)
 	assert.Regexp(t, "FF10386.*type", err)
@@ -297,13 +333,13 @@ func TestIdentityPluginMissingType(t *testing.T) {
 
 func TestIdentityPluginBadName(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	or.database = or.mdi
 	or.identityPlugins = nil
 	iifactory.InitConfig(identityConfig)
 	identityConfig.AddKnownKey(coreconfig.PluginConfigName, "wrong//")
 	identityConfig.AddKnownKey(coreconfig.PluginConfigType, "tbd")
 	config.Set("plugins.identity", []fftypes.JSONObject{{}})
-	or.mdi.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
 	ctx, cancelCtx := context.WithCancel(context.Background())
 	err := or.Init(ctx, cancelCtx)
 	assert.Regexp(t, "FF00140.*name", err)
@@ -311,13 +347,13 @@ func TestIdentityPluginBadName(t *testing.T) {
 
 func TestIdentityPluginUnknownPlugin(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	or.database = or.mdi
 	or.identityPlugins = nil
 	iifactory.InitConfig(identityConfig)
 	identityConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip")
 	identityConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong")
 	config.Set("plugins.identity", []fftypes.JSONObject{{}})
-	or.mdi.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
 	ctx, cancelCtx := context.WithCancel(context.Background())
 	err := or.Init(ctx, cancelCtx)
 	assert.Regexp(t, "FF10212.*wrong", err)
@@ -325,19 +361,14 @@ func TestIdentityPluginUnknownPlugin(t *testing.T) {
 
 func TestIdentityPlugin(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	or.database = or.mdi
 	or.identityPlugins = nil
 	iifactory.InitConfig(identityConfig)
 	identityConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip")
 	identityConfig.AddKnownKey(coreconfig.PluginConfigType, "onchain")
 	config.Set("plugins.identity", []fftypes.JSONObject{{}})
-	or.mdi.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mii.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mps.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mdi.On("GetIdentities", mock.Anything, mock.Anything).Return([]*core.Identity{}, nil, nil)
-	or.mdi.On("GetNamespace", mock.Anything, mock.Anything).Return(nil, nil)
-	or.mdi.On("UpsertNamespace", mock.Anything, mock.Anything, true).Return(nil)
+	or.mns.On("Init", mock.Anything, or.mdi).Return(nil)
 	ctx, cancelCtx := context.WithCancel(context.Background())
 	err := or.Init(ctx, cancelCtx)
 	assert.NoError(t, err)
@@ -345,6 +376,7 @@ func TestIdentityPlugin(t *testing.T) {
 
 func TestBadIdentityInitFail(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	or.blockchains = nil
 	config.Set("plugins.identity", []fftypes.JSONObject{{}})
 	iifactory.InitConfig(identityConfig)
@@ -361,10 +393,10 @@ func TestBadIdentityInitFail(t *testing.T) {
 
 func TestBadDeprecatedBlockchainPlugin(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	deprecatedBlockchainConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong")
 	or.blockchains = nil
-	or.mdi.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mii.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
+	or.databases["database_0"] = or.mdi
 	ctx, cancelCtx := context.WithCancel(context.Background())
 	err := or.Init(ctx, cancelCtx)
 	assert.Regexp(t, "FF10110.*wrong", err)
@@ -372,11 +404,11 @@ func TestBadDeprecatedBlockchainPlugin(t *testing.T) {
 
 func TestDeprecatedBlockchainInitFail(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	bifactory.InitConfigDeprecated(deprecatedBlockchainConfig)
 	deprecatedBlockchainConfig.AddKnownKey(coreconfig.PluginConfigType, "ethereum")
 	or.blockchains = nil
-	or.mii.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mdi.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
+	or.databases["database_0"] = or.mdi
 	ctx, cancelCtx := context.WithCancel(context.Background())
 	err := or.Init(ctx, cancelCtx)
 	assert.Regexp(t, "FF10138.*url", err)
@@ -384,11 +416,11 @@ func TestDeprecatedBlockchainInitFail(t *testing.T) {
 
 func TestBlockchainGetPlugins(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	bifactory.InitConfig(blockchainConfig)
 	config.Set("plugins.blockchain", []fftypes.JSONObject{{}})
 	blockchainConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip")
 	blockchainConfig.AddKnownKey(coreconfig.PluginConfigType, "ethereum")
-	or.mdi.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
 	ctx := context.Background()
 	plugins, err := or.getBlockchainPlugins(ctx)
 	assert.Equal(t, 1, len(plugins))
@@ -397,6 +429,7 @@ func TestBlockchainGetPlugins(t *testing.T) {
 
 func TestBlockchainGetPluginsNoType(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	bifactory.InitConfig(blockchainConfig)
 	config.Set("plugins.blockchain", []fftypes.JSONObject{{}})
 	blockchainConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip")
@@ -407,6 +440,7 @@ func TestBlockchainGetPluginsNoType(t *testing.T) {
 
 func TestBlockchainGetPluginsBadName(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	bifactory.InitConfig(blockchainConfig)
 	config.Set("plugins.blockchain", []fftypes.JSONObject{{}})
 	blockchainConfig.AddKnownKey(coreconfig.PluginConfigName, "wrong/////////////")
@@ -418,13 +452,12 @@ func TestBlockchainGetPluginsBadName(t *testing.T) {
 
 func TestBlockchainGetPluginsBadPlugin(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	bifactory.InitConfig(blockchainConfig)
 	config.Set("plugins.blockchain", []fftypes.JSONObject{{}})
 	blockchainConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip")
 	blockchainConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong//")
 	or.blockchains = nil
-	or.mii.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mdi.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
 	ctx := context.Background()
 	err := or.initPlugins(ctx)
 	assert.Error(t, err)
@@ -432,6 +465,7 @@ func TestBlockchainGetPluginsBadPlugin(t *testing.T) {
 
 func TestBlockchainInitPlugins(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	bifactory.InitConfig(blockchainConfig)
 	config.Set("plugins.blockchain", []fftypes.JSONObject{{}})
 	blockchainConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip")
@@ -447,6 +481,7 @@ func TestBlockchainInitPlugins(t *testing.T) {
 
 func TestDeprecatedBlockchainInitPlugin(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	bifactory.InitConfigDeprecated(deprecatedBlockchainConfig)
 	deprecatedBlockchainConfig.AddKnownKey(coreconfig.PluginConfigType, "ethereum")
 	or.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
@@ -457,6 +492,7 @@ func TestDeprecatedBlockchainInitPlugin(t *testing.T) {
 
 func TestBlockchainInitPluginsFail(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	bifactory.InitConfig(blockchainConfig)
 	config.Set("plugins.blockchain", []fftypes.JSONObject{{}})
 	blockchainConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip")
@@ -465,9 +501,6 @@ func TestBlockchainInitPluginsFail(t *testing.T) {
 	blockchainConfig.AddKnownKey("ethconnect.url", "")
 	or.blockchains = nil
 
-	or.mii.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mdi.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-
 	ctx := context.Background()
 	err := or.initPlugins(ctx)
 	assert.Regexp(t, "FF10138.*url", err)
@@ -475,6 +508,7 @@ func TestBlockchainInitPluginsFail(t *testing.T) {
 
 func TestBadSharedStoragePlugin(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	ssfactory.InitConfig(sharedstorageConfig)
 	sharedstorageConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong")
 	config.Set("plugins.sharedstorage", []fftypes.JSONObject{{}})
@@ -487,15 +521,13 @@ func TestBadSharedStoragePlugin(t *testing.T) {
 
 func TestBadSharedStoragePluginType(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	or.sharedstoragePlugins = nil
 	or.database = or.mdi
 	ssfactory.InitConfig(sharedstorageConfig)
 	sharedstorageConfig.AddKnownKey(coreconfig.PluginConfigName, "sharedstorage")
 	sharedstorageConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong")
 	config.Set("plugins.sharedstorage", []fftypes.JSONObject{{}})
-	or.mdi.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mii.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
 
 	ctx, cancelCtx := context.WithCancel(context.Background())
 	err := or.Init(ctx, cancelCtx)
@@ -504,6 +536,7 @@ func TestBadSharedStoragePluginType(t *testing.T) {
 
 func TestBadSharedStoragePluginName(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	ssfactory.InitConfig(sharedstorageConfig)
 	sharedstorageConfig.AddKnownKey(coreconfig.PluginConfigName, "wrong////")
 	sharedstorageConfig.AddKnownKey(coreconfig.PluginConfigType, "ipfs")
@@ -517,6 +550,7 @@ func TestBadSharedStoragePluginName(t *testing.T) {
 
 func TestSharedStorageInitPlugins(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	ssfactory.InitConfig(sharedstorageConfig)
 	config.Set("plugins.sharedstorage", []fftypes.JSONObject{{}})
 	sharedstorageConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip")
@@ -532,15 +566,13 @@ func TestSharedStorageInitPlugins(t *testing.T) {
 
 func TestSharedStorageInitPluginsFail(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	or.sharedstoragePlugins = nil
 	or.database = or.mdi
 	ssfactory.InitConfig(sharedstorageConfig)
 	config.Set("plugins.sharedstorage", []fftypes.JSONObject{{}})
 	sharedstorageConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip")
 	sharedstorageConfig.AddKnownKey(coreconfig.PluginConfigType, "ipfs")
-	or.mii.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mdi.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
 	ctx := context.Background()
 	err := or.initPlugins(ctx)
 	assert.Regexp(t, "FF10138.*url", err)
@@ -548,6 +580,7 @@ func TestSharedStorageInitPluginsFail(t *testing.T) {
 
 func TestDeprecatedSharedStorageInitPlugin(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	ssfactory.InitConfigDeprecated(deprecatedSharedStorageConfig)
 	deprecatedSharedStorageConfig.AddKnownKey(coreconfig.PluginConfigType, "ipfs")
 	or.mps.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
@@ -558,11 +591,10 @@ func TestDeprecatedSharedStorageInitPlugin(t *testing.T) {
 
 func TestDeprecatedSharedStorageInitPluginFail(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	or.sharedstoragePlugins = nil
 	ssfactory.InitConfigDeprecated(deprecatedSharedStorageConfig)
 	deprecatedSharedStorageConfig.AddKnownKey(coreconfig.PluginConfigType, "ipfs")
-	or.mdi.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mii.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
 	ctx := context.Background()
 	err := or.initPlugins(ctx)
 	assert.Regexp(t, "FF10138.*url", err)
@@ -570,10 +602,9 @@ func TestDeprecatedSharedStorageInitPluginFail(t *testing.T) {
 
 func TestBadDeprecatedSharedStoragePlugin(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	deprecatedSharedStorageConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong")
 	or.sharedstoragePlugins = nil
-	or.mdi.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mii.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
 	ctx, cancelCtx := context.WithCancel(context.Background())
 	err := or.Init(ctx, cancelCtx)
 	assert.Regexp(t, "FF10134.*Unknown", err)
@@ -581,16 +612,13 @@ func TestBadDeprecatedSharedStoragePlugin(t *testing.T) {
 
 func TestBadDataExchangePlugin(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	dxfactory.InitConfig(dataexchangeConfig)
 	dataexchangeConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip")
 	dataexchangeConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong//")
 	config.Set("plugins.dataexchange", []fftypes.JSONObject{{}})
 	or.database = or.mdi
 	or.dataexchangePlugins = nil
-	or.mdi.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mii.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mps.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
 	ctx, cancelCtx := context.WithCancel(context.Background())
 	err := or.Init(ctx, cancelCtx)
 	assert.Regexp(t, "FF10213.*wrong", err)
@@ -598,16 +626,13 @@ func TestBadDataExchangePlugin(t *testing.T) {
 
 func TestDataExchangePluginBadName(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	dxfactory.InitConfig(dataexchangeConfig)
 	dataexchangeConfig.AddKnownKey(coreconfig.PluginConfigName, "wrong//")
 	dataexchangeConfig.AddKnownKey(coreconfig.PluginConfigType, "ffdx")
 	config.Set("plugins.dataexchange", []fftypes.JSONObject{{}})
 	or.database = or.mdi
 	or.dataexchangePlugins = nil
-	or.mdi.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mii.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mps.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
 	ctx, cancelCtx := context.WithCancel(context.Background())
 	err := or.Init(ctx, cancelCtx)
 	assert.Regexp(t, "FF00140.*name", err)
@@ -615,15 +640,12 @@ func TestDataExchangePluginBadName(t *testing.T) {
 
 func TestDataExchangePluginMissingName(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	dxfactory.InitConfig(dataexchangeConfig)
 	dataexchangeConfig.AddKnownKey(coreconfig.PluginConfigType, "ffdx")
 	config.Set("plugins.dataexchange", []fftypes.JSONObject{{}})
 	or.database = or.mdi
 	or.dataexchangePlugins = nil
-	or.mdi.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mii.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mps.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
 	ctx, cancelCtx := context.WithCancel(context.Background())
 	err := or.Init(ctx, cancelCtx)
 	assert.Regexp(t, "FF10386.*name", err)
@@ -631,19 +653,14 @@ func TestDataExchangePluginMissingName(t *testing.T) {
 
 func TestBadDataExchangeInitFail(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	dxfactory.InitConfig(dataexchangeConfig)
 	dataexchangeConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip")
 	dataexchangeConfig.AddKnownKey(coreconfig.PluginConfigType, "ffdx")
 	config.Set("plugins.dataexchange", []fftypes.JSONObject{{}})
 	or.database = or.mdi
 	or.dataexchangePlugins = nil
-	or.mdi.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mii.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mps.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
 	or.mdi.On("GetIdentities", mock.Anything, mock.Anything).Return([]*core.Identity{}, nil, nil)
-	or.mdi.On("GetNamespace", mock.Anything, mock.Anything).Return(nil, nil)
-	or.mdi.On("UpsertNamespace", mock.Anything, mock.Anything, true).Return(nil)
 	ctx, cancelCtx := context.WithCancel(context.Background())
 	err := or.Init(ctx, cancelCtx)
 	assert.Regexp(t, "FF10138.*url", err)
@@ -651,17 +668,12 @@ func TestBadDataExchangeInitFail(t *testing.T) {
 
 func TestDeprecatedBadDataExchangeInitFail(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	dxfactory.InitConfigDeprecated(deprecatedDataexchangeConfig)
 	deprecatedDataexchangeConfig.AddKnownKey(coreconfig.PluginConfigType, "ffdx")
 	or.database = or.mdi
 	or.dataexchangePlugins = nil
-	or.mdi.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mii.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mps.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
 	or.mdi.On("GetIdentities", mock.Anything, mock.Anything).Return([]*core.Identity{}, nil, nil)
-	or.mdi.On("GetNamespace", mock.Anything, mock.Anything).Return(nil, nil)
-	or.mdi.On("UpsertNamespace", mock.Anything, mock.Anything, true).Return(nil)
 	ctx, cancelCtx := context.WithCancel(context.Background())
 	err := or.Init(ctx, cancelCtx)
 	assert.Regexp(t, "FF10138.*url", err)
@@ -683,17 +695,12 @@ func TestDeprecatedDataExchangeInit(t *testing.T) {
 
 func TestDeprecatedBadDataExchangePlugin(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	dxfactory.InitConfigDeprecated(deprecatedDataexchangeConfig)
 	deprecatedDataexchangeConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong//")
 	or.database = or.mdi
 	or.dataexchangePlugins = nil
-	or.mdi.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mii.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mps.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
 	or.mdi.On("GetIdentities", mock.Anything, mock.Anything).Return([]*core.Identity{}, nil, nil)
-	or.mdi.On("GetNamespace", mock.Anything, mock.Anything).Return(nil, nil)
-	or.mdi.On("UpsertNamespace", mock.Anything, mock.Anything, true).Return(nil)
 	ctx, cancelCtx := context.WithCancel(context.Background())
 	err := or.Init(ctx, cancelCtx)
 	assert.Regexp(t, "FF10213.*wrong", err)
@@ -701,20 +708,12 @@ func TestDeprecatedBadDataExchangePlugin(t *testing.T) {
 
 func TestTokensMissingName(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	tifactory.InitConfig(tokensConfig)
 	tokensConfig.AddKnownKey(coreconfig.PluginConfigType, "fftokens")
 	config.Set("plugins.tokens", []fftypes.JSONObject{{}})
 	or.database = or.mdi
 	or.tokens = nil
-	or.mdi.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mii.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mps.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mdi.On("GetIdentities", mock.Anything, mock.Anything).Return([]*core.Identity{}, nil, nil)
-	or.mdx.On("InitConfig", mock.Anything).Return()
-	or.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mdi.On("GetNamespace", mock.Anything, mock.Anything).Return(nil, nil)
-	or.mdi.On("UpsertNamespace", mock.Anything, mock.Anything, true).Return(nil)
 	ctx, cancelCtx := context.WithCancel(context.Background())
 	err := or.Init(ctx, cancelCtx)
 	assert.Regexp(t, "FF10386.*type", err)
@@ -722,21 +721,13 @@ func TestTokensMissingName(t *testing.T) {
 
 func TestTokensBadName(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	tifactory.InitConfig(tokensConfig)
 	tokensConfig.AddKnownKey(coreconfig.PluginConfigName, "/////////////")
 	tokensConfig.AddKnownKey(coreconfig.PluginConfigType, "fftokens")
 	config.Set("plugins.tokens", []fftypes.JSONObject{{}})
 	or.database = or.mdi
 	or.tokens = nil
-	or.mdi.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mii.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mps.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mdi.On("GetIdentities", mock.Anything, mock.Anything).Return([]*core.Identity{}, nil, nil)
-	or.mdx.On("InitConfig", mock.Anything).Return()
-	or.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mdi.On("GetNamespace", mock.Anything, mock.Anything).Return(nil, nil)
-	or.mdi.On("UpsertNamespace", mock.Anything, mock.Anything, true).Return(nil)
 	ctx, cancelCtx := context.WithCancel(context.Background())
 	err := or.Init(ctx, cancelCtx)
 	assert.Regexp(t, "FF00140.*name", err)
@@ -744,21 +735,13 @@ func TestTokensBadName(t *testing.T) {
 
 func TestBadTokensPlugin(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	tifactory.InitConfig(tokensConfig)
 	tokensConfig.AddKnownKey(coreconfig.PluginConfigName, "erc20_erc721")
 	tokensConfig.AddKnownKey(coreconfig.PluginConfigType, "fftokens")
 	config.Set("plugins.tokens", []fftypes.JSONObject{{}})
 	or.database = or.mdi
 	or.tokens = nil
-	or.mdi.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mii.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mps.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mdi.On("GetIdentities", mock.Anything, mock.Anything).Return([]*core.Identity{}, nil, nil)
-	or.mdx.On("InitConfig", mock.Anything).Return()
-	or.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mdi.On("GetNamespace", mock.Anything, mock.Anything).Return(nil, nil)
-	or.mdi.On("UpsertNamespace", mock.Anything, mock.Anything, true).Return(nil)
 	ctx, cancelCtx := context.WithCancel(context.Background())
 	err := or.Init(ctx, cancelCtx)
 	assert.Error(t, err)
@@ -766,6 +749,7 @@ func TestBadTokensPlugin(t *testing.T) {
 
 func TestGoodTokensPlugin(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	tifactory.InitConfig(tokensConfig)
 	tokensConfig.AddKnownKey(coreconfig.PluginConfigName, "erc20_erc721")
 	tokensConfig.AddKnownKey(coreconfig.PluginConfigType, "fftokens")
@@ -773,15 +757,7 @@ func TestGoodTokensPlugin(t *testing.T) {
 	config.Set("plugins.tokens", []fftypes.JSONObject{{}})
 	or.database = or.mdi
 	or.tokens = nil
-	or.mdi.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mii.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mps.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mdi.On("GetIdentities", mock.Anything, mock.Anything).Return([]*core.Identity{}, nil, nil)
-	or.mdx.On("InitConfig", mock.Anything).Return()
-	or.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mdi.On("GetNamespace", mock.Anything, mock.Anything).Return(nil, nil)
-	or.mdi.On("UpsertNamespace", mock.Anything, mock.Anything, true).Return(nil)
+	or.mns.On("Init", mock.Anything, or.mdi).Return(nil)
 	ctx, cancelCtx := context.WithCancel(context.Background())
 	err := or.Init(ctx, cancelCtx)
 	assert.NoError(t, err)
@@ -789,21 +765,13 @@ func TestGoodTokensPlugin(t *testing.T) {
 
 func TestBadDeprecatedTokensPluginNoName(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	tifactory.InitConfigDeprecated(deprecatedTokensConfig)
 	deprecatedTokensConfig.AddKnownKey(coreconfig.PluginConfigName)
 	deprecatedTokensConfig.AddKnownKey(tokens.TokensConfigPlugin, "wrong")
 	config.Set("tokens", []fftypes.JSONObject{{}})
 	or.database = or.mdi
 	or.tokens = nil
-	or.mdi.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mii.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mps.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mdi.On("GetIdentities", mock.Anything, mock.Anything).Return([]*core.Identity{}, nil, nil)
-	or.mdx.On("InitConfig", mock.Anything).Return()
-	or.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mdi.On("GetNamespace", mock.Anything, mock.Anything).Return(nil, nil)
-	or.mdi.On("UpsertNamespace", mock.Anything, mock.Anything, true).Return(nil)
 	ctx, cancelCtx := context.WithCancel(context.Background())
 	err := or.Init(ctx, cancelCtx)
 	assert.Regexp(t, "FF10273", err)
@@ -811,21 +779,13 @@ func TestBadDeprecatedTokensPluginNoName(t *testing.T) {
 
 func TestBadDeprecatedTokensPluginInvalidName(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	tifactory.InitConfigDeprecated(deprecatedTokensConfig)
 	deprecatedTokensConfig.AddKnownKey(coreconfig.PluginConfigName, "!wrong")
 	deprecatedTokensConfig.AddKnownKey(tokens.TokensConfigPlugin, "text")
 	config.Set("tokens", []fftypes.JSONObject{{}})
 	or.database = or.mdi
 	or.tokens = nil
-	or.mdi.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mii.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mps.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mdi.On("GetIdentities", mock.Anything, mock.Anything).Return([]*core.Identity{}, nil, nil)
-	or.mdx.On("InitConfig", mock.Anything).Return()
-	or.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mdi.On("GetNamespace", mock.Anything, mock.Anything).Return(nil, nil)
-	or.mdi.On("UpsertNamespace", mock.Anything, mock.Anything, true).Return(nil)
 	ctx, cancelCtx := context.WithCancel(context.Background())
 	err := or.Init(ctx, cancelCtx)
 	assert.Regexp(t, "FF00140.*'name'", err)
@@ -833,22 +793,13 @@ func TestBadDeprecatedTokensPluginInvalidName(t *testing.T) {
 
 func TestBadDeprecatedTokensPluginNoType(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	tifactory.InitConfigDeprecated(deprecatedTokensConfig)
 	deprecatedTokensConfig.AddKnownKey(coreconfig.PluginConfigName, "text")
 	deprecatedTokensConfig.AddKnownKey(tokens.TokensConfigPlugin)
 	config.Set("tokens", []fftypes.JSONObject{{}})
 	or.database = or.mdi
 	or.tokens = nil
-	or.mdi.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mii.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mbi.On("VerifyIdentitySyntax", mock.Anything, mock.Anything, mock.Anything).Return("", nil)
-	or.mps.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mdi.On("GetIdentities", mock.Anything, mock.Anything).Return([]*core.Identity{}, nil, nil)
-	or.mdx.On("InitConfig", mock.Anything).Return()
-	or.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mdi.On("GetNamespace", mock.Anything, mock.Anything).Return(nil, nil)
-	or.mdi.On("UpsertNamespace", mock.Anything, mock.Anything, true).Return(nil)
 	ctx, cancelCtx := context.WithCancel(context.Background())
 	err := or.Init(ctx, cancelCtx)
 	assert.Regexp(t, "FF10272", err)
@@ -856,6 +807,7 @@ func TestBadDeprecatedTokensPluginNoType(t *testing.T) {
 
 func TestGoodDeprecatedTokensPlugin(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	deprecatedTokensConfig = config.RootArray("tokens")
 	tifactory.InitConfigDeprecated(deprecatedTokensConfig)
 	deprecatedTokensConfig.AddKnownKey(coreconfig.PluginConfigName, "test")
@@ -864,15 +816,7 @@ func TestGoodDeprecatedTokensPlugin(t *testing.T) {
 	config.Set("tokens", []fftypes.JSONObject{{}})
 	or.database = or.mdi
 	or.tokens = nil
-	or.mdi.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mii.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mps.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mdi.On("GetIdentities", mock.Anything, mock.Anything).Return([]*core.Identity{}, nil, nil)
-	or.mdx.On("InitConfig", mock.Anything).Return()
-	or.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mdi.On("GetNamespace", mock.Anything, mock.Anything).Return(nil, nil)
-	or.mdi.On("UpsertNamespace", mock.Anything, mock.Anything, true).Return(nil)
+	or.mns.On("Init", mock.Anything, or.mdi).Return(nil)
 	ctx, cancelCtx := context.WithCancel(context.Background())
 	err := or.Init(ctx, cancelCtx)
 	assert.NoError(t, err)
@@ -880,6 +824,7 @@ func TestGoodDeprecatedTokensPlugin(t *testing.T) {
 
 func TestInitMessagingComponentFail(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	or.databases = nil
 	or.messaging = nil
 	err := or.initComponents(context.Background())
@@ -888,6 +833,7 @@ func TestInitMessagingComponentFail(t *testing.T) {
 
 func TestInitEventsComponentFail(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	or.databases = nil
 	or.events = nil
 	err := or.initComponents(context.Background())
@@ -896,6 +842,7 @@ func TestInitEventsComponentFail(t *testing.T) {
 
 func TestInitNetworkMapComponentFail(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	or.databases = nil
 	or.networkmap = nil
 	err := or.initComponents(context.Background())
@@ -904,6 +851,7 @@ func TestInitNetworkMapComponentFail(t *testing.T) {
 
 func TestInitOperationComponentFail(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	or.databases = nil
 	or.operations = nil
 	err := or.initComponents(context.Background())
@@ -912,6 +860,7 @@ func TestInitOperationComponentFail(t *testing.T) {
 
 func TestInitSharedStorageDownloadComponentFail(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	or.databases = nil
 	or.sharedDownload = nil
 	err := or.initComponents(context.Background())
@@ -920,13 +869,15 @@ func TestInitSharedStorageDownloadComponentFail(t *testing.T) {
 
 func TestInitAdminEventsInit(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	or.adminEvents = nil
-	err := or.initComponents(context.Background())
+	err := or.initPlugins(context.Background())
 	assert.NoError(t, err)
 }
 
 func TestInitBatchComponentFail(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	or.databases = nil
 	or.batch = nil
 	err := or.initComponents(context.Background())
@@ -935,6 +886,7 @@ func TestInitBatchComponentFail(t *testing.T) {
 
 func TestInitBroadcastComponentFail(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	or.databases = nil
 	or.broadcast = nil
 	err := or.initComponents(context.Background())
@@ -943,6 +895,7 @@ func TestInitBroadcastComponentFail(t *testing.T) {
 
 func TestInitDataComponentFail(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	or.databases = nil
 	or.data = nil
 	err := or.initComponents(context.Background())
@@ -951,6 +904,7 @@ func TestInitDataComponentFail(t *testing.T) {
 
 func TestInitIdentityComponentFail(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	or.databases = nil
 	or.identity = nil
 	or.txHelper = nil
@@ -960,6 +914,7 @@ func TestInitIdentityComponentFail(t *testing.T) {
 
 func TestInitAssetsComponentFail(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	or.databases = nil
 	or.assets = nil
 	err := or.initComponents(context.Background())
@@ -968,6 +923,7 @@ func TestInitAssetsComponentFail(t *testing.T) {
 
 func TestInitContractsComponentFail(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	or.databases = nil
 	or.contracts = nil
 	err := or.initComponents(context.Background())
@@ -976,6 +932,7 @@ func TestInitContractsComponentFail(t *testing.T) {
 
 func TestInitDefinitionsComponentFail(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	or.databases = nil
 	or.definitions = nil
 	err := or.initComponents(context.Background())
@@ -984,6 +941,7 @@ func TestInitDefinitionsComponentFail(t *testing.T) {
 
 func TestInitBatchPinComponentFail(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	or.databases = nil
 	or.batchpin = nil
 	err := or.initComponents(context.Background())
@@ -992,17 +950,26 @@ func TestInitBatchPinComponentFail(t *testing.T) {
 
 func TestInitOperationsComponentFail(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	or.databases = nil
 	or.operations = nil
 	err := or.initComponents(context.Background())
 	assert.Regexp(t, "FF10128", err)
 }
 
+func TestInitNamespaceComponentFail(t *testing.T) {
+	or := newTestOrchestrator()
+	defer or.cleanup(t)
+	or.namespace = nil
+	err := or.initNamespaces(context.Background())
+	assert.Regexp(t, "FF10166", err)
+}
+
 func TestStartBatchFail(t *testing.T) {
 	coreconfig.Reset()
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	or.mba.On("Start").Return(fmt.Errorf("pop"))
-	or.mbi.On("Start").Return(nil)
 	err := or.Start()
 	assert.EqualError(t, err, "pop")
 }
@@ -1010,12 +977,12 @@ func TestStartBatchFail(t *testing.T) {
 func TestStartTokensFail(t *testing.T) {
 	coreconfig.Reset()
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	or.mbi.On("Start").Return(nil)
 	or.mba.On("Start").Return(nil)
 	or.mem.On("Start").Return(nil)
 	or.mbm.On("Start").Return(nil)
 	or.mpm.On("Start").Return(nil)
-	or.mam.On("Start").Return(nil)
 	or.msd.On("Start").Return(nil)
 	or.mom.On("Start").Return(nil)
 	or.mti.On("Start").Return(fmt.Errorf("pop"))
@@ -1026,6 +993,7 @@ func TestStartTokensFail(t *testing.T) {
 func TestStartBlockchainsFail(t *testing.T) {
 	coreconfig.Reset()
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	or.mbi.On("Start").Return(fmt.Errorf("pop"))
 	or.mba.On("Start").Return(nil)
 	err := or.Start()
@@ -1035,22 +1003,18 @@ func TestStartBlockchainsFail(t *testing.T) {
 func TestStartStopOk(t *testing.T) {
 	coreconfig.Reset()
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	or.mbi.On("Start").Return(nil)
 	or.mba.On("Start").Return(nil)
 	or.mem.On("Start").Return(nil)
 	or.mbm.On("Start").Return(nil)
 	or.mpm.On("Start").Return(nil)
-	or.mam.On("Start").Return(nil)
 	or.mti.On("Start").Return(nil)
 	or.mmi.On("Start").Return(nil)
 	or.msd.On("Start").Return(nil)
 	or.mom.On("Start").Return(nil)
-	or.mbi.On("WaitStop").Return(nil)
 	or.mba.On("WaitStop").Return(nil)
-	or.mem.On("WaitStop").Return(nil)
 	or.mbm.On("WaitStop").Return(nil)
-	or.mam.On("WaitStop").Return(nil)
-	or.mti.On("WaitStop").Return(nil)
 	or.mdm.On("WaitStop").Return(nil)
 	or.msd.On("WaitStop").Return(nil)
 	or.mom.On("WaitStop").Return(nil)
@@ -1061,81 +1025,13 @@ func TestStartStopOk(t *testing.T) {
 	or.WaitStop() // swallows dups
 }
 
-func TestInitNamespacesBadName(t *testing.T) {
-	or := newTestOrchestrator()
-	coreconfig.Reset()
-	config.Set(coreconfig.NamespacesPredefined, fftypes.JSONObjectArray{
-		{"name": "!Badness"},
-	})
-	err := or.initNamespaces(context.Background())
-	assert.Regexp(t, "FF00140", err)
-}
-
-func TestInitNamespacesGetFail(t *testing.T) {
-	or := newTestOrchestrator()
-	or.database = or.mdi
-	or.mdi.On("GetNamespace", mock.Anything, mock.Anything).Return(nil, fmt.Errorf("pop"))
-	err := or.initNamespaces(context.Background())
-	assert.Regexp(t, "pop", err)
-}
-
-func TestInitNamespacesUpsertFail(t *testing.T) {
-	or := newTestOrchestrator()
-	or.database = or.mdi
-	or.mdi.On("GetNamespace", mock.Anything, mock.Anything).Return(nil, nil)
-	or.mdi.On("UpsertNamespace", mock.Anything, mock.Anything, true).Return(fmt.Errorf("pop"))
-	err := or.initNamespaces(context.Background())
-	assert.Regexp(t, "pop", err)
-}
-
-func TestInitNamespacesUpsertNotNeeded(t *testing.T) {
-	or := newTestOrchestrator()
-	or.database = or.mdi
-	or.mdi.On("GetNamespace", mock.Anything, mock.Anything).Return(&core.Namespace{
-		Type: core.NamespaceTypeBroadcast, // any broadcasted NS will not be updated
-	}, nil)
-	err := or.initNamespaces(context.Background())
-	assert.NoError(t, err)
-}
-
-func TestInitNamespacesDefaultMissing(t *testing.T) {
-	or := newTestOrchestrator()
-	or.database = or.mdi
-	config.Set(coreconfig.NamespacesPredefined, fftypes.JSONObjectArray{})
-	err := or.initNamespaces(context.Background())
-	assert.Regexp(t, "FF10166", err)
-}
-
-func TestInitNamespacesDupName(t *testing.T) {
-	or := newTestOrchestrator()
-	namespaceConfig.AddKnownKey("predefined.0.name", "ns1")
-	namespaceConfig.AddKnownKey("predefined.1.name", "ns2")
-	namespaceConfig.AddKnownKey("predefined.2.name", "ns2")
-	config.Set(coreconfig.NamespacesDefault, "ns1")
-	nsList, err := or.getPredefinedNamespaces(context.Background())
-	assert.NoError(t, err)
-	assert.Len(t, nsList, 3)
-	assert.Equal(t, core.SystemNamespace, nsList[0].Name)
-	assert.Equal(t, "ns1", nsList[1].Name)
-	assert.Equal(t, "ns2", nsList[2].Name)
-}
-
 func TestInitOK(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	or.database = or.mdi
-	or.mdi.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mii.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mps.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mdi.On("GetIdentities", mock.Anything, mock.Anything).Return([]*core.Identity{}, nil, nil)
-	or.mdx.On("InitConfig", mock.Anything).Return()
-	or.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mdi.On("GetNamespace", mock.Anything, mock.Anything).Return(nil, nil)
-	or.mdi.On("UpsertNamespace", mock.Anything, mock.Anything, true).Return(nil)
-	or.mti.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mmi.On("Init").Return(nil)
 	err := config.ReadConfig("core", configDir+"/firefly.core.yaml")
 	assert.NoError(t, err)
+	or.mns.On("Init", mock.Anything, or.mdi).Return(nil)
 	ctx, cancelCtx := context.WithCancel(context.Background())
 	err = or.Init(ctx, cancelCtx)
 	assert.NoError(t, err)
@@ -1155,21 +1051,12 @@ func TestInitOK(t *testing.T) {
 
 func TestInitOKWithMetrics(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	or.metrics = nil
 	or.database = or.mdi
-	or.mdi.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mii.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mps.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mdi.On("GetIdentities", mock.Anything, mock.Anything).Return([]*core.Identity{}, nil, nil)
-	or.mdx.On("InitConfig", mock.Anything).Return()
-	or.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mdi.On("GetNamespace", mock.Anything, mock.Anything).Return(nil, nil)
-	or.mdi.On("UpsertNamespace", mock.Anything, mock.Anything, true).Return(nil)
-	or.mti.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	or.mmi.On("Init").Return(nil)
 	err := config.ReadConfig("core", configDir+"/firefly.core.yaml")
 	assert.NoError(t, err)
+	or.mns.On("Init", mock.Anything, or.mdi).Return(nil)
 	ctx, cancelCtx := context.WithCancel(context.Background())
 	err = or.Init(ctx, cancelCtx)
 	assert.NoError(t, err)
@@ -1186,8 +1073,21 @@ func TestInitOKWithMetrics(t *testing.T) {
 	assert.Equal(t, or.mae, or.AdminEvents())
 }
 
+func TestInitNamespaceFail(t *testing.T) {
+	or := newTestOrchestrator()
+	defer or.cleanup(t)
+	or.database = or.mdi
+	err := config.ReadConfig("core", configDir+"/firefly.core.yaml")
+	assert.NoError(t, err)
+	or.mns.On("Init", mock.Anything, or.mdi).Return(fmt.Errorf("pop"))
+	ctx, cancelCtx := context.WithCancel(context.Background())
+	err = or.Init(ctx, cancelCtx)
+	assert.EqualError(t, err, "pop")
+}
+
 func TestInitDataExchangeGetNodesFail(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	or.database = or.mdi
 
 	or.mdi.On("GetIdentities", mock.Anything, mock.Anything).Return(nil, nil, fmt.Errorf("pop"))
@@ -1198,6 +1098,7 @@ func TestInitDataExchangeGetNodesFail(t *testing.T) {
 
 func TestInitDataExchangeWithNodes(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	or.database = or.mdi
 	dxfactory.InitConfig(dataexchangeConfig)
 	dataexchangeConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip")
@@ -1206,8 +1107,6 @@ func TestInitDataExchangeWithNodes(t *testing.T) {
 	config.Set("plugins.dataexchange", []fftypes.JSONObject{{}})
 
 	or.mdi.On("GetIdentities", mock.Anything, mock.Anything).Return([]*core.Identity{{}}, nil, nil)
-	or.mdx.On("InitConfig", mock.Anything).Return()
-	or.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
 
 	err := or.initDataExchange(or.ctx)
 	assert.NoError(t, err)