1 module tagion.testbench.services.recorder_service;
2 // Default import list for bdd
3 import std.algorithm;
4 import std.array;
5 import std.random;
6 import std.stdio;
7 import std.typecons : Tuple;
8 import tagion.actor;
9 import tagion.behaviour;
10 import tagion.crypto.SecureInterfaceNet;
11 import tagion.crypto.SecureNet : StdSecureNet;
12 import tagion.crypto.Types;
13 import tagion.dart.Recorder;
14 import tagion.hibon.Document;
15 import tagion.hibon.HiBONRecord;
16 import tagion.recorderchain.RecorderChain;
17 import tagion.recorderchain.RecorderChainBlock : RecorderChainBlock;
18 import tagion.services.messages;
19 import tagion.services.replicator;
20 import tagion.testbench.dart.dart_helper_functions;
21 import tagion.testbench.tools.Environment;
22 import tagion.utils.pretend_safe_concurrency;
23 
24 enum feature = Feature(
25             "Recorder chain service",
26             [
27         "This services should store the recorder for each epoch in chain as a file.",
28         "This is an extension of the Recorder backup chain."
29 ]);
30 
31 alias FeatureContext = Tuple!(
32         StoreOfTheRecorderChain, "StoreOfTheRecorderChain",
33         FeatureGroup*, "result"
34 );
35 
36 @safe @Scenario("store of the recorder chain",
37         [])
38 class StoreOfTheRecorderChain {
39     immutable(ReplicatorOptions) replicator_opts;
40     SecureNet replicator_net;
41     ActorHandle handle;
42     Mt19937 gen;
43     RandomArchives random_archives;
44     RecordFactory.Recorder insert_recorder;
45     Document[] docs;
46     RecordFactory record_factory;
47     RecorderChainBlock block;
48 
49     struct SimpleDoc {
50         ulong n;
51         mixin HiBONRecord!(q{
52             this(ulong n) {
53                 this.n = n;
54             }
55         });
56     }
57 
58     this(immutable(ReplicatorOptions) replicator_opts) {
59         this.replicator_opts = replicator_opts;
60         replicator_net = new StdSecureNet();
61         record_factory = RecordFactory(replicator_net);
62         replicator_net.generateKeyPair("recordernet very secret");
63         gen = Mt19937(4321);
64     }
65 
66     @Given("a epoch recorder with epoch number has been received")
67     Document received() {
68         thisActor.task_name = "recorder_supervisor";
69         register(thisActor.task_name, thisTid);
70         handle = spawn!ReplicatorService("ReplicatorService", replicator_opts);
71         waitforChildren(Ctrl.ALIVE);
72 
73         random_archives = RandomArchives(gen.front, 4, 10);
74         insert_recorder = record_factory.recorder;
75         docs = (() @trusted => cast(Document[]) random_archives.values.map!(a => SimpleDoc(a).toDoc).array)();
76 
77         insert_recorder.insert(docs, Archive.Type.ADD);
78         auto send_recorder = SendRecorder();
79 
80         Fingerprint dummy_bullseye = Fingerprint([1, 2, 3, 4]);
81         block = new RecorderChainBlock(insert_recorder.toDoc, Fingerprint.init, dummy_bullseye, 0, replicator_net);
82 
83         (() @trusted => handle.send(send_recorder, cast(immutable) insert_recorder, dummy_bullseye, immutable long(0)))();
84 
85         import core.thread;
86         import core.time;
87 
88         (() @trusted => Thread.sleep(5.msecs))();
89 
90         return result_ok;
91     }
92 
93     @When("the recorder has been store to a file")
94     Document file() @trusted {
95         RecorderChainStorage storage = new RecorderChainFileStorage(replicator_opts.folder_path, replicator_net);
96         RecorderChain recorder_chain = new RecorderChain(storage);
97 
98         check(recorder_chain.getLastBlock.getHash == block.getHash, "read block not the same");
99         return result_ok;
100     }
101 
102     @Then("the file should be checked")
103     Document checked() {
104         handle.send(Sig.STOP);
105         waitforChildren(Ctrl.END);
106         return result_ok;
107     }
108 
109 }