diff --git a/test/integration/compose/network.spec.ts b/test/integration/compose/network.spec.ts new file mode 100644 index 00000000..7280fb3b --- /dev/null +++ b/test/integration/compose/network.spec.ts @@ -0,0 +1,188 @@ +import { expect } from 'chai'; + +import { Network } from '~/src/compose/network'; +import { createNetwork, withMockerode } from '~/test-lib/mockerode'; + +import * as Docker from 'dockerode'; + +describe('compose/network: integration tests', () => { + const docker = new Docker(); + after(async () => { + const allNetworks = await docker.listNetworks(); + + // Delete any remaining networks + await Promise.all( + allNetworks + .filter(({ Name }) => !['bridge', 'host', 'none'].includes(Name)) // exclude docker default network from the cleanup + .map(({ Name }) => docker.getNetwork(Name).remove()), + ); + }); + + describe('creating and removing networks', () => { + // This tests the happy path on the engine, including create and remove + it('creates a new network on the engine with the given data', async () => { + const network = Network.fromComposeObject('default', 12345, 'deadbeef', { + ipam: { + driver: 'default', + config: [ + { + subnet: '172.20.0.0/16', + ip_range: '172.20.10.0/24', + gateway: '172.20.0.1', + }, + ], + options: {}, + }, + }); + + // Create the network + await network.create(); + + const dockerNetworkName = Network.generateDockerName( + network.appUuid!, + network.name, + ); + // This should not throw + const dockerNetwork = await docker + .getNetwork(dockerNetworkName) + .inspect(); + + // Check that the create function was called with proper arguments + expect(dockerNetwork).to.deep.include({ + Name: 'deadbeef_default', + Driver: 'bridge', + IPAM: { + Driver: 'default', + Config: [ + { + Subnet: '172.20.0.0/16', + IPRange: '172.20.10.0/24', + Gateway: '172.20.0.1', + }, + ], + Options: {}, + }, + EnableIPv6: false, + Internal: false, + Labels: { + 'io.balena.supervised': 'true', + 'io.balena.app-id': '12345', + }, + Options: {}, + }); + + // Test network removal + await network.remove(); + + // The network should no longer exist + await expect(docker.getNetwork(dockerNetwork).inspect()).to.be.rejected; + }); + + it('throws the error if there is a problem while creating the network', async () => { + await withMockerode(async (mockerode) => { + const network = Network.fromComposeObject( + 'default', + 12345, + 'deadbeef', + { + ipam: { + driver: 'default', + config: [ + { + subnet: '172.20.0.0/16', + ip_range: '172.20.10.0/24', + gateway: '172.20.0.1', + }, + ], + options: {}, + }, + }, + ); + + // Re-define the dockerode.createNetwork to throw + mockerode.createNetwork.rejects('Unknown engine error'); + + // Creating the network should fail + return expect(network.create()).to.be.rejected; + }); + }); + }); + + describe('removing a network', () => { + it('removes a legacy network from the engine if it exists', async () => { + // Creates a legacy network + await docker.createNetwork({ Name: '12345_default' }); + + // Create a dummy network object + const network = Network.fromComposeObject( + 'default', + 12345, + 'deadbeef', + {}, + ); + + // Perform the operation + await network.remove(); + + await expect(docker.getNetwork('12345_default').inspect()).to.be.rejected; + }); + + it('ignores the request if the given network does not exist on the engine', async () => { + // Create a mock network to add to the mock engine + await docker.createNetwork({ + Name: 'some_network', + }); + + // Create a dummy network object + const network = Network.fromComposeObject( + 'default', + 12345, + 'deadbeef', + {}, + ); + + // This should not fail + await expect(network.remove()).to.not.be.rejected; + + // We expect the network state to remain constant + await expect(docker.getNetwork('some_network').inspect()).to.not.be + .rejected; + + // Cleanup + await docker.getNetwork('some_network').remove(); + }); + + it('throws the error if there is a problem while removing the network', async () => { + // Create a mock network to add to the mock engine + const mockNetwork = createNetwork({ + Id: 'aaaaaaaa', + Name: 'a173bdb734884b778f5cc3dffd18733e_default', + Labels: { + 'io.balena.app-id': '12345', + }, + }); + + await withMockerode( + async (mockerode) => { + // We can change the return value of the mockerode removeNetwork + // to have the remove operation fail + mockerode.removeNetwork.throws({ + statusCode: 500, + message: 'Failed to remove the network', + }); + + // Create a dummy network object + const network = Network.fromComposeObject( + 'default', + 12345, + 'a173bdb734884b778f5cc3dffd18733e', + {}, + ); + + await expect(network.remove()).to.be.rejected; + }, + { networks: [mockNetwork] }, + ); + }); + }); +}); diff --git a/test/legacy/src/compose/network.spec.ts b/test/unit/compose/network.spec.ts similarity index 70% rename from test/legacy/src/compose/network.spec.ts rename to test/unit/compose/network.spec.ts index cc510cce..c4f1b9e6 100644 --- a/test/legacy/src/compose/network.spec.ts +++ b/test/unit/compose/network.spec.ts @@ -3,11 +3,10 @@ import * as sinon from 'sinon'; import { Network } from '~/src/compose/network'; import { NetworkInspectInfo } from 'dockerode'; -import { createNetwork, withMockerode } from '~/test-lib/mockerode'; import { log } from '~/lib/supervisor-console'; -describe('compose/network', () => { +describe('compose/network: unit tests', () => { describe('creating a network from a compose object', () => { it('creates a default network configuration if no config is given', () => { const network = Network.fromComposeObject( @@ -509,221 +508,4 @@ describe('compose/network', () => { ).to.be.false; }); }); - - describe('creating networks', () => { - it('creates a new network on the engine with the given data', async () => { - await withMockerode(async (mockerode) => { - const network = Network.fromComposeObject( - 'default', - 12345, - 'deadbeef', - { - ipam: { - driver: 'default', - config: [ - { - subnet: '172.20.0.0/16', - ip_range: '172.20.10.0/24', - gateway: '172.20.0.1', - }, - ], - options: {}, - }, - }, - ); - - // Create the network - await network.create(); - - // Check that the create function was called with proper arguments - expect(mockerode.createNetwork).to.have.been.calledOnceWith({ - Name: 'deadbeef_default', - Driver: 'bridge', - CheckDuplicate: true, - IPAM: { - Driver: 'default', - Config: [ - { - Subnet: '172.20.0.0/16', - IPRange: '172.20.10.0/24', - Gateway: '172.20.0.1', - }, - ], - Options: {}, - }, - EnableIPv6: false, - Internal: false, - Labels: { - 'io.balena.supervised': 'true', - 'io.balena.app-id': '12345', - }, - Options: {}, - }); - }); - }); - - it('throws the error if there is a problem while creating the network', async () => { - await withMockerode(async (mockerode) => { - const network = Network.fromComposeObject( - 'default', - 12345, - 'deadbeef', - { - ipam: { - driver: 'default', - config: [ - { - subnet: '172.20.0.0/16', - ip_range: '172.20.10.0/24', - gateway: '172.20.0.1', - }, - ], - options: {}, - }, - }, - ); - - // Re-define the dockerode.createNetwork to throw - mockerode.createNetwork.rejects('Unknown engine error'); - - // Creating the network should fail - return expect(network.create()).to.be.rejected.then((error) => - expect(error).to.have.property('name', 'Unknown engine error'), - ); - }); - }); - }); - - describe('removing a network', () => { - it('removes the legacy network from the engine if it exists', async () => { - // Create a mock network to add to the mock engine - const dockerNetwork = createNetwork({ - Id: 'aaaaaaa', - Name: '12345_default', - }); - - await withMockerode( - async (mockerode) => { - // Check that the engine has the network - expect(await mockerode.listNetworks()).to.have.lengthOf(1); - - // Create a dummy network object - const network = Network.fromComposeObject( - 'default', - 12345, - 'deadbeef', - {}, - ); - - // Perform the operation - await network.remove(); - - // The removal step should delete the object from the engine data - expect(mockerode.removeNetwork).to.have.been.calledOnceWith( - 'aaaaaaa', - ); - }, - { networks: [dockerNetwork] }, - ); - }); - - it('removes the network from the engine if it exists', async () => { - // Create a mock network to add to the mock engine - const dockerNetwork = createNetwork({ - Id: 'deadbeef', - Name: 'a173bdb734884b778f5cc3dffd18733e_default', - Labels: { - 'io.balena.supervised': 'true', - 'io.balena.app-id': '12345', - }, - }); - - await withMockerode( - async (mockerode) => { - // Check that the engine has the network - expect(await mockerode.listNetworks()).to.have.lengthOf(1); - - // Create a dummy network object - const network = Network.fromComposeObject( - 'default', - 12345, - 'a173bdb734884b778f5cc3dffd18733e', - {}, - ); - - // Perform the operation - await network.remove(); - - // The removal step should delete the object from the engine data - expect(mockerode.removeNetwork).to.have.been.calledOnceWith( - 'deadbeef', - ); - }, - { networks: [dockerNetwork] }, - ); - }); - - it('ignores the request if the given network does not exist on the engine', async () => { - // Create a mock network to add to the mock engine - const mockNetwork = createNetwork({ - Id: 'aaaaaaaa', - Name: 'some_network', - }); - - await withMockerode( - async (mockerode) => { - // Check that the engine has the network - expect(await mockerode.listNetworks()).to.have.lengthOf(1); - - // Create a dummy network object - const network = Network.fromComposeObject( - 'default', - 12345, - 'deadbeef', - {}, - ); - - // This should not fail - await expect(network.remove()).to.not.be.rejected; - - // We expect the network state to remain constant - expect(await mockerode.listNetworks()).to.have.lengthOf(1); - }, - { networks: [mockNetwork] }, - ); - }); - - it('throws the error if there is a problem while removing the network', async () => { - // Create a mock network to add to the mock engine - const mockNetwork = createNetwork({ - Id: 'aaaaaaaa', - Name: 'a173bdb734884b778f5cc3dffd18733e_default', - Labels: { - 'io.balena.app-id': '12345', - }, - }); - - await withMockerode( - async (mockerode) => { - // We can change the return value of the mockerode removeNetwork - // to have the remove operation fail - mockerode.removeNetwork.throws({ - statusCode: 500, - message: 'Failed to remove the network', - }); - - // Create a dummy network object - const network = Network.fromComposeObject( - 'default', - 12345, - 'a173bdb734884b778f5cc3dffd18733e', - {}, - ); - - await expect(network.remove()).to.be.rejected; - }, - { networks: [mockNetwork] }, - ); - }); - }); });