-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'develop' as v0.6 release
- Loading branch information
Showing
23 changed files
with
1,729 additions
and
1,764 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
209 changes: 209 additions & 0 deletions
209
integration/src/it/scala/datomisca/AccountsSampleSpec.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,209 @@ | ||
/* | ||
* Copyright 2012 Pellucid and Zenexity | ||
* | ||
* 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 datomisca | ||
|
||
import org.scalatest.{FlatSpec, Matchers} | ||
|
||
import scala.concurrent.ExecutionContext.Implicits.global | ||
|
||
|
||
class AccountsSampleSpec | ||
extends FlatSpec | ||
with Matchers | ||
with DatomicFixture | ||
with AwaitHelper | ||
{ | ||
|
||
object AccountsSchema { | ||
// Namespaces | ||
val account = new Namespace("account") | ||
val trans = new Namespace("trans") | ||
val fn = new Namespace("fn") | ||
|
||
// Attributes | ||
val name = Attribute(account / "name", SchemaType.string, Cardinality.one) | ||
.withDoc("The name of the account") | ||
.withUnique(Unique.value) | ||
.withFullText(true) | ||
val balance = Attribute(account / "balance", SchemaType.bigdec, Cardinality.one) | ||
.withDoc("The account balance") | ||
val minBalance = Attribute(account / "min-balance", SchemaType.bigdec, Cardinality.one) | ||
.withDoc("The minimum permitted balance for the account") | ||
|
||
val amount = Attribute(trans / "amount", SchemaType.bigdec, Cardinality.one) | ||
.withDoc("The transaction amount") | ||
val from = Attribute(trans / "from", SchemaType.ref, Cardinality.one) | ||
.withDoc("The sending account") | ||
val to = Attribute(trans / "to", SchemaType.ref, Cardinality.one) | ||
.withDoc("The receiving account") | ||
|
||
|
||
val creditFn = AddTxFunction.typed[Long, BigDecimal](fn / "credit") ("db", "id", "amount") ("clojure") {s""" | ||
(let [e (d/entity db id) | ||
min-balance (${minBalance} e 0) | ||
balance (+ (${balance} e 0) amount) ] | ||
(if (>= balance min-balance) | ||
[[:db/add id ${balance} balance]] | ||
(throw (Exception. "Insufficient funds")))) | ||
"""} | ||
|
||
val transferFn = AddTxFunction.typed[Long, Long, BigDecimal](fn / "transfer") ("db", "from", "to", "amount") ("clojure") {s""" | ||
[[${creditFn.ident} from (- amount)] | ||
[${creditFn.ident} to amount]] | ||
"""} | ||
|
||
// Schema | ||
val schema = Seq( | ||
name, balance, minBalance, | ||
amount, from, to, | ||
creditFn, transferFn | ||
) | ||
} | ||
|
||
|
||
object AccountsTxData { | ||
import AccountsSchema._ | ||
|
||
val issuer = ( | ||
SchemaEntity.newBuilder | ||
+= (name -> "Issuer") | ||
+= (balance -> BigDecimal(0)) | ||
+= (minBalance -> BigDecimal(-1000)) | ||
) withId DId(Partition.USER) | ||
|
||
val bob = ( | ||
SchemaEntity.newBuilder | ||
+= (AccountsSchema.name -> "Bob") | ||
+= (balance -> BigDecimal(0)) | ||
+= (minBalance -> BigDecimal(0)) | ||
) withId DId(Partition.USER) | ||
|
||
val alice = ( | ||
SchemaEntity.newBuilder | ||
+= (AccountsSchema.name -> "Alice") | ||
+= (balance -> BigDecimal(0)) | ||
+= (minBalance -> BigDecimal(0)) | ||
) withId DId(Partition.USER) | ||
|
||
val sampleTxData = Seq(issuer, bob, alice) | ||
|
||
def transfer(fromAcc: Long, toAcc: Long, transAmount: BigDecimal, note: String): Seq[Operation] = { | ||
val txId = DId(Partition.TX) | ||
Seq( | ||
InvokeTxFunction(transferFn)(fromAcc, toAcc, transAmount), | ||
Fact.add(txId)(Attribute.doc -> note), | ||
(SchemaEntity.newBuilder | ||
+= (from -> fromAcc) | ||
+= (to -> toAcc) | ||
+= (amount -> transAmount) | ||
) withId txId | ||
) | ||
} | ||
|
||
def credit(toAcc: Long, amount: BigDecimal): Operation = | ||
InvokeTxFunction(creditFn)(toAcc, amount) | ||
} | ||
|
||
|
||
object AccountsQueries { | ||
import AccountsSchema._ | ||
|
||
val queryAccounts = Query(s""" | ||
[:find ?a | ||
:in $$ | ||
:where [?a ${name}]] | ||
""") | ||
|
||
val findAccountByName = Query(s""" | ||
[ | ||
:find ?a | ||
:in $$ ?name | ||
:where | ||
[?a ${name} ?name] | ||
] | ||
""") | ||
|
||
val queryAllTransactions = Query(s""" | ||
[:find ?tx | ||
:in $$ | ||
:where | ||
[?tx ${amount}]] | ||
""") | ||
|
||
val rulesParty = Query.rules(s""" | ||
[[[party ?t ?a] | ||
[?t ${from} ?a]] | ||
[[party ?t ?a] | ||
[?t ${to} ?a]]] | ||
""") | ||
|
||
val queryAccountTransactions = Query(""" | ||
[:find ?t | ||
:in $ % ?a | ||
:where | ||
(party ?t ?a)] | ||
""") | ||
} | ||
|
||
|
||
"Accounts Sample" should "run to completion" in withDatomicDB { implicit conn => | ||
import AccountsSchema.{name, from, to} | ||
import AccountsQueries._ | ||
import AccountsTxData.{transfer, credit} | ||
|
||
await { | ||
Datomic.transact(AccountsSchema.schema) | ||
} | ||
await { | ||
Datomic.transact(AccountsTxData.sampleTxData) | ||
} | ||
|
||
val DLong(issuerId) = | ||
Datomic.q(findAccountByName, conn.database, DString("Issuer")).head | ||
val DLong(bobId) = | ||
Datomic.q(findAccountByName, conn.database, DString("Bob")).head | ||
val DLong(aliceId) = | ||
Datomic.q(findAccountByName, conn.database, DString("Alice")).head | ||
|
||
await { | ||
Datomic.transact(transfer(issuerId, aliceId, BigDecimal(77), "Issuance to Alice")) | ||
} | ||
await { | ||
Datomic.transact(transfer(issuerId, bobId, BigDecimal(23), "Issuance to Bob")) | ||
} | ||
await { | ||
Datomic.transact(transfer(aliceId, bobId, BigDecimal(7), "Dinner")) | ||
} | ||
|
||
Datomic.q(queryAccounts, conn.database) should have size (3) | ||
|
||
Datomic.q(queryAllTransactions, conn.database) should have size (3) | ||
|
||
Datomic.q(queryAccountTransactions, conn.database, rulesParty, DLong(issuerId)) should have size (2) | ||
|
||
Datomic.q(queryAccountTransactions, conn.database, rulesParty, DLong(bobId)) should have size (2) | ||
|
||
Datomic.q(queryAccountTransactions, conn.database, rulesParty, DLong(aliceId)) should have size (2) | ||
|
||
an [Exception] should be thrownBy { | ||
await { | ||
Datomic.transact(transfer(aliceId, bobId, BigDecimal(71), "Car")) | ||
} | ||
} | ||
} | ||
|
||
} |
144 changes: 144 additions & 0 deletions
144
integration/src/it/scala/datomisca/AggregatesSpec.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
/* | ||
* Copyright 2012 Pellucid and Zenexity | ||
* | ||
* 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 datomisca | ||
|
||
import org.scalatest.{FlatSpec, Matchers, OptionValues} | ||
|
||
|
||
class AggregatesSpec | ||
extends FlatSpec | ||
with Matchers | ||
with OptionValues | ||
with DatomicFixture | ||
{ | ||
|
||
val countObjects = Query(""" | ||
[:find (count ?e) | ||
:where [?e :object/name ?n]] | ||
""") | ||
|
||
val findLargestRadius = Query(""" | ||
[:find (max ?radius) | ||
:where [_ :object/meanRadius ?radius]] | ||
""") | ||
|
||
val findSmallestRadius = Query(""" | ||
[:find (min ?radius) | ||
:where [_ :object/meanRadius ?radius]] | ||
""") | ||
|
||
val findAverageRadius = Query(""" | ||
[:find (avg ?radius) | ||
:with ?e | ||
:where [?e :object/meanRadius ?radius]] | ||
""") | ||
|
||
val findMedianRadius = Query(""" | ||
[:find (median ?radius) | ||
:with ?e | ||
:where [?e :object/meanRadius ?radius]] | ||
""") | ||
|
||
val findStdDevOfRadius = Query(""" | ||
[:find (stddev ?radius) | ||
:with ?e | ||
:where [?e :object/meanRadius ?radius]] | ||
""") | ||
|
||
val findRandomObject = Query(""" | ||
[:find (rand ?name) | ||
:where [?e :object/name ?name]] | ||
""") | ||
|
||
val findSmallest3 = Query(""" | ||
[:find (min 3 ?radius) | ||
:with ?e | ||
:where [?e :object/meanRadius ?radius]] | ||
""") | ||
|
||
val findLargest3 = Query(""" | ||
[:find (max 3 ?radius) | ||
:with ?e | ||
:where [?e :object/meanRadius ?radius]] | ||
""") | ||
|
||
val findRandom5 = Query(""" | ||
[:find (rand 5 ?name) | ||
:with ?e | ||
:where [?e :object/name ?name]] | ||
""") | ||
|
||
val choose5 = Query(""" | ||
[:find (sample 5 ?name) | ||
:with ?e | ||
:where [?e :object/name ?name]] | ||
""") | ||
|
||
val findAvgSchemaNameLength = Query(""" | ||
[:find (avg ?length) | ||
:with ?e | ||
:where | ||
[?e :db/ident ?ident] | ||
[(name ?ident) ?name] | ||
[(count ?name) ?length]] | ||
""") | ||
|
||
val countAttributesAndValueTypesInSchema = Query(""" | ||
[:find (count ?a) (count-distinct ?vt) | ||
:where | ||
[?a :db/ident ?ident] | ||
[?a :db/valueType ?vt]] | ||
""") | ||
|
||
"Aggregates examples" should "run to completion" in withSampleDatomicDB(PlutoSampleData) { conn => | ||
val db = conn.database | ||
|
||
Datomic.q(countObjects, db).headOption.value should equal (DLong(17)) | ||
|
||
Datomic.q(findLargestRadius, db).headOption.value should equal (DDouble(696000.0)) | ||
|
||
Datomic.q(findSmallestRadius, db).headOption.value should equal (DDouble(1163.0)) | ||
|
||
Datomic.q(findAverageRadius, db).headOption.value should equal (DDouble(53390.17647058824)) | ||
|
||
Datomic.q(findMedianRadius, db).headOption.value should equal (DDouble(2631.2)) | ||
|
||
Datomic.q(findStdDevOfRadius, db).headOption.value should equal (DDouble(161902.5278094546)) | ||
|
||
Datomic.q(findRandomObject, db) should have size (1) | ||
|
||
Datomic.q(findSmallest3, db).headOption.value match { | ||
case DColl(coll) => | ||
coll should contain allOf (DDouble(1163.0), DDouble(1353.4), DDouble(1561.0)) | ||
case _ => fail | ||
} | ||
|
||
Datomic.q(findLargest3, db).headOption.value match { | ||
case DColl(coll) => | ||
coll should contain allOf (DDouble(696000.0), DDouble(69911.0), DDouble(58232.0)) | ||
case _ => fail | ||
} | ||
|
||
Datomic.q(findRandom5, db) should have size (1) | ||
|
||
Datomic.q(choose5, db) should have size (1) | ||
|
||
Datomic.q(findAvgSchemaNameLength, db).headOption.value should equal (DDouble(5.846153846153846)) | ||
|
||
Datomic.q(countAttributesAndValueTypesInSchema, db) should have size (1) | ||
} | ||
} |
Oops, something went wrong.