1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* Copyright 2017 American Express Travel Related Services Company, Inc. |
5
|
|
|
* |
6
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
7
|
|
|
* you may not use this file except in compliance with the License. |
8
|
|
|
* You may obtain a copy of the License at |
9
|
|
|
* |
10
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0 |
11
|
|
|
* |
12
|
|
|
* Unless required by applicable law or agreed to in writing, software |
13
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS, |
14
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
15
|
|
|
* or implied. See the License for the specific language governing |
16
|
|
|
* permissions and limitations under the License. |
17
|
|
|
*/ |
18
|
|
|
|
19
|
|
|
declare(strict_types=1); |
20
|
|
|
|
21
|
|
|
namespace AmericanExpressTest\HyperledgerFabricClient\Channel; |
22
|
|
|
|
23
|
|
|
use AmericanExpress\HyperledgerFabricClient\Chaincode\Chaincode; |
24
|
|
|
use AmericanExpress\HyperledgerFabricClient\Channel\Channel; |
25
|
|
|
use AmericanExpress\HyperledgerFabricClient\Exception\InvalidArgumentException; |
26
|
|
|
use AmericanExpress\HyperledgerFabricClient\Exception\RuntimeException; |
27
|
|
|
use AmericanExpress\HyperledgerFabricClient\Peer\PeerInterface; |
28
|
|
|
use AmericanExpress\HyperledgerFabricClient\Proposal\ResponseCollection; |
29
|
|
|
use AmericanExpress\HyperledgerFabricClient\Proposal\ProposalProcessorInterface; |
30
|
|
|
use AmericanExpress\HyperledgerFabricClient\ProtoFactory\ChaincodeHeaderExtensionFactory; |
31
|
|
|
use AmericanExpress\HyperledgerFabricClient\ProtoFactory\ChaincodeProposalPayloadFactory; |
32
|
|
|
use AmericanExpress\HyperledgerFabricClient\Transaction\TransactionOptions; |
33
|
|
|
use AmericanExpress\HyperledgerFabricClient\Identity\SerializedIdentityAwareHeaderGeneratorInterface; |
34
|
|
|
use AmericanExpressTest\HyperledgerFabricClient\TestAsset\MockProposalProcessor; |
35
|
|
|
use Hyperledger\Fabric\Protos\Peer\ChaincodeID; |
36
|
|
|
use PHPUnit\Framework\TestCase; |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* @covers \AmericanExpress\HyperledgerFabricClient\Channel\Channel |
40
|
|
|
*/ |
41
|
|
|
class ChannelTest extends TestCase |
42
|
|
|
{ |
43
|
|
|
/** |
44
|
|
|
* @var PeerInterface|\PHPUnit_Framework_MockObject_MockObject |
45
|
|
|
*/ |
46
|
|
|
private $peer; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* @var ProposalProcessorInterface|\PHPUnit_Framework_MockObject_MockObject |
50
|
|
|
*/ |
51
|
|
|
private $client; |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* @var SerializedIdentityAwareHeaderGeneratorInterface|\PHPUnit_Framework_MockObject_MockObject |
55
|
|
|
*/ |
56
|
|
|
private $headerGenerator; |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* @var Channel |
60
|
|
|
*/ |
61
|
|
|
private $sut; |
62
|
|
|
|
63
|
|
|
protected function setUp() |
64
|
|
|
{ |
65
|
|
|
$this->client = $this->getMockBuilder(ProposalProcessorInterface::class) |
66
|
|
|
->getMock(); |
67
|
|
|
|
68
|
|
|
$this->headerGenerator = $this->getMockBuilder(SerializedIdentityAwareHeaderGeneratorInterface::class) |
69
|
|
|
->getMock(); |
70
|
|
|
|
71
|
|
|
$this->peer = $this->getMockBuilder(PeerInterface::class) |
72
|
|
|
->getMock(); |
73
|
|
|
|
74
|
|
|
$this->sut = new Channel('foo', $this->client, $this->headerGenerator); |
75
|
|
|
} |
76
|
|
|
|
77
|
|
|
public function testPeersIsEmptyCollectionByDefault() |
78
|
|
|
{ |
79
|
|
|
self::assertCount(0, $this->sut->getPeers()); |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
public function testCanSpecifyInitialPeers() |
83
|
|
|
{ |
84
|
|
|
$this->sut = new Channel( |
85
|
|
|
'foo', |
86
|
|
|
$this->client, |
87
|
|
|
$this->headerGenerator, |
88
|
|
|
[$this->peer] |
89
|
|
|
); |
90
|
|
|
self::assertCount(1, $this->sut->getPeers()); |
91
|
|
|
self::assertContains($this->peer, $this->sut->getPeers()); |
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
public function testCanAddOnePeer() |
95
|
|
|
{ |
96
|
|
|
$this->sut->addPeers($this->peer); |
97
|
|
|
|
98
|
|
|
self::assertCount(1, $this->sut->getPeers()); |
99
|
|
|
self::assertContains($this->peer, $this->sut->getPeers()); |
100
|
|
|
} |
101
|
|
|
|
102
|
|
View Code Duplication |
public function testCanAddManyPeers() |
|
|
|
|
103
|
|
|
{ |
104
|
|
|
$peer = $this->getMockBuilder(PeerInterface::class) |
105
|
|
|
->getMock(); |
106
|
|
|
|
107
|
|
|
$this->sut->addPeers($this->peer, $peer); |
108
|
|
|
|
109
|
|
|
self::assertCount(2, $this->sut->getPeers()); |
110
|
|
|
self::assertContains($this->peer, $this->sut->getPeers()); |
111
|
|
|
self::assertContains($peer, $this->sut->getPeers()); |
112
|
|
|
} |
113
|
|
|
|
114
|
|
View Code Duplication |
public function testCanSetManyPeers() |
|
|
|
|
115
|
|
|
{ |
116
|
|
|
$peer = $this->getMockBuilder(PeerInterface::class) |
117
|
|
|
->getMock(); |
118
|
|
|
|
119
|
|
|
$this->sut->setPeers([ |
120
|
|
|
$this->peer, |
121
|
|
|
$peer, |
122
|
|
|
]); |
123
|
|
|
|
124
|
|
|
self::assertCount(2, $this->sut->getPeers()); |
125
|
|
|
self::assertContains($this->peer, $this->sut->getPeers()); |
126
|
|
|
self::assertContains($peer, $this->sut->getPeers()); |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
public function testChannelCanCreateChaincode() |
130
|
|
|
{ |
131
|
|
|
$chainCode = $this->sut->getChaincode('FizBuz'); |
132
|
|
|
|
133
|
|
|
self::assertInstanceOf(Chaincode::class, $chainCode); |
134
|
|
|
self::assertSame($chainCode->getName(), 'FizBuz'); |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
/** |
138
|
|
|
* @expectedException InvalidArgumentException |
139
|
|
|
*/ |
140
|
|
|
public function testChannelThrowsExceptionOnEmptyChaincodeName() |
141
|
|
|
{ |
142
|
|
|
$this->sut->getChaincode(''); |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
public function testChannelCanProcessChaincodeProposal() |
146
|
|
|
{ |
147
|
|
|
$this->client->method('processProposal') |
|
|
|
|
148
|
|
|
->willReturn($proposalResponse = new ResponseCollection()); |
149
|
|
|
|
150
|
|
|
$result = $this->doChaincodeProposal(new TransactionOptions([ |
151
|
|
|
'peers' => [$this->peer], |
152
|
|
|
])); |
153
|
|
|
self::assertSame($proposalResponse, $result); |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
/** |
157
|
|
|
* @expectedException RuntimeException |
158
|
|
|
*/ |
159
|
|
|
public function testChannelWillWrapExceptionOnProcessChaincodeProposal() |
160
|
|
|
{ |
161
|
|
|
$this->client->method('processProposal') |
162
|
|
|
->willThrowException(new InvalidArgumentException()); |
163
|
|
|
|
164
|
|
|
$this->doChaincodeProposal(new TransactionOptions([ |
165
|
|
|
'peers' => [$this->peer], |
166
|
|
|
])); |
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
public function testChannelCanProcessChaincodeProposalWithDefaultPeers() |
170
|
|
|
{ |
171
|
|
|
$this->sut = new Channel( |
172
|
|
|
'foo', |
173
|
|
|
$processor = new MockProposalProcessor(), |
174
|
|
|
$this->headerGenerator, |
175
|
|
|
[$this->peer] |
176
|
|
|
); |
177
|
|
|
|
178
|
|
|
$this->doChaincodeProposal(null, $this->sut); |
179
|
|
|
self::assertContains($this->peer, $processor->getTransactionOptions()->getPeers()); |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
public function testChannelCanProcessChaincodeProposalAndOverrideDefaultPeers() |
183
|
|
|
{ |
184
|
|
|
$this->sut = new Channel( |
185
|
|
|
'foo', |
186
|
|
|
$processor = new MockProposalProcessor(), |
187
|
|
|
$this->headerGenerator |
188
|
|
|
); |
189
|
|
|
|
190
|
|
|
$transactionOptions = new TransactionOptions(); |
191
|
|
|
$transactionOptions->addPeers($this->peer); |
192
|
|
|
|
193
|
|
|
$this->doChaincodeProposal($transactionOptions); |
194
|
|
|
self::assertContains($this->peer, $processor->getTransactionOptions()->getPeers()); |
195
|
|
|
} |
196
|
|
|
|
197
|
|
|
/** |
198
|
|
|
* @expectedException RuntimeException |
199
|
|
|
*/ |
200
|
|
|
public function testThrowsRuntimeExceptionOnMissingPeers() |
201
|
|
|
{ |
202
|
|
|
$this->client->method('processProposal') |
203
|
|
|
->willReturn($proposalResponse = new ResponseCollection()); |
204
|
|
|
|
205
|
|
|
$this->doChaincodeProposal(); |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
public function createChaincodeHeaderExtension() |
209
|
|
|
{ |
210
|
|
|
return ChaincodeHeaderExtensionFactory::fromChaincodeId( |
211
|
|
|
(new ChaincodeID()) |
212
|
|
|
->setPath('FizBuz') |
213
|
|
|
->setName('FooBar') |
214
|
|
|
->setVersion('v12.34') |
215
|
|
|
); |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
public function createChaincodeProposalPayload() |
219
|
|
|
{ |
220
|
|
|
return ChaincodeProposalPayloadFactory::fromChaincodeInvocationSpecArgs([ |
221
|
|
|
'foo' => 'bar', |
222
|
|
|
]); |
223
|
|
|
} |
224
|
|
|
|
225
|
|
|
public function doChaincodeProposal(TransactionOptions $options = null, Channel $channel = null) |
226
|
|
|
{ |
227
|
|
|
if ($channel === null) { |
228
|
|
|
$channel = $this->sut; |
229
|
|
|
} |
230
|
|
|
|
231
|
|
|
return $channel->processChaincodeProposal( |
232
|
|
|
$this->createChaincodeProposalPayload(), |
233
|
|
|
$this->createChaincodeHeaderExtension(), |
234
|
|
|
$options |
235
|
|
|
); |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
/** |
239
|
|
|
* @expectedException \AmericanExpress\HyperledgerFabricClient\Exception\InvalidArgumentException |
240
|
|
|
*/ |
241
|
|
|
public function testFailToCreateChannelWithInvalidPeers() |
242
|
|
|
{ |
243
|
|
|
new Channel('foo', $this->client, $this->headerGenerator, [new \stdClass()]); |
244
|
|
|
} |
245
|
|
|
} |
246
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.