| /* |
| * Copyright 2015-2016 IBM Corporation |
| * |
| * 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 whisk.core.entity.test |
| |
| import java.time.Instant |
| |
| import scala.Vector |
| import scala.concurrent.Await |
| |
| import org.junit.runner.RunWith |
| import org.scalatest.BeforeAndAfter |
| import org.scalatest.BeforeAndAfterAll |
| import org.scalatest.FlatSpec |
| import org.scalatest.junit.JUnitRunner |
| |
| import common.StreamLogging |
| import common.WskActorSystem |
| import whisk.core.WhiskConfig |
| import whisk.core.database.DocumentConflictException |
| import whisk.core.database.NoDocumentException |
| import whisk.core.database.test.DbUtils |
| import whisk.core.entity._ |
| |
| @RunWith(classOf[JUnitRunner]) |
| class DatastoreTests extends FlatSpec |
| with BeforeAndAfter |
| with BeforeAndAfterAll |
| with WskActorSystem |
| with DbUtils |
| with StreamLogging { |
| |
| val namespace = EntityPath("test namespace") |
| val config = new WhiskConfig(WhiskAuthStore.requiredProperties ++ WhiskEntityStore.requiredProperties) |
| val datastore = WhiskEntityStore.datastore(config) |
| val authstore = WhiskAuthStore.datastore(config) |
| |
| override def afterAll() { |
| println("Shutting down store connections") |
| datastore.shutdown() |
| authstore.shutdown() |
| super.afterAll() |
| } |
| |
| @volatile var counter = 0 |
| def aname(implicit n: EntityName) = { |
| counter = counter + 1 |
| EntityName(s"$n$counter") |
| } |
| |
| def afullname(implicit namespace: EntityPath, name: String) = FullyQualifiedEntityName(namespace, EntityName(name)) |
| |
| after { |
| cleanup() |
| } |
| |
| behavior of "Datastore" |
| |
| it should "CRD auth" in { |
| implicit val tid = transid() |
| val auth = WhiskAuth(Subject(), AuthKey()) |
| putGetCheck(authstore, auth, WhiskAuth) |
| } |
| |
| it should "CRD action blackbox" in { |
| implicit val tid = transid() |
| implicit val basename = EntityName("create action blackbox") |
| val exec = Exec.bb("image") |
| val actions = Seq( |
| WhiskAction(namespace, aname, exec), |
| WhiskAction(namespace, aname, exec, Parameters("x", "y")), |
| WhiskAction(namespace, aname, exec, Parameters("x", "y")), |
| WhiskAction(namespace, aname, exec, Parameters("x", "y") ++ Parameters("x", "y")), |
| WhiskAction(namespace, aname, exec, Parameters("x", "y") ++ Parameters("y", "x"))) |
| val docs = actions.map { entity => |
| putGetCheck(datastore, entity, WhiskAction) |
| } |
| } |
| |
| it should "CRD action js" in { |
| implicit val tid = transid() |
| implicit val basename = EntityName("create action js") |
| val exec = Exec.js("code") |
| val actions = Seq( |
| WhiskAction(namespace, aname, exec, Parameters()), |
| WhiskAction(namespace, aname, exec, Parameters("x", "y")), |
| WhiskAction(namespace, aname, exec, Parameters("x", "y")), |
| WhiskAction(namespace, aname, exec, Parameters("x", "y") ++ Parameters("x", "y")), |
| WhiskAction(namespace, aname, exec, Parameters("x", "y") ++ Parameters("y", "x"))) |
| val docs = actions.map { entity => |
| putGetCheck(datastore, entity, WhiskAction) |
| } |
| } |
| |
| it should "CRD trigger" in { |
| implicit val tid = transid() |
| implicit val basename = EntityName("create trigger") |
| val triggers = Seq( |
| WhiskTrigger(namespace, aname), |
| WhiskTrigger(namespace, aname, Parameters("x", "y")), |
| WhiskTrigger(namespace, aname, Parameters("x", "y"))) |
| val docs = triggers.map { entity => |
| putGetCheck(datastore, entity, WhiskTrigger) |
| } |
| } |
| |
| it should "CRD rule" in { |
| implicit val tid = transid() |
| implicit val basename = EntityName("create rule") |
| val rules = Seq( |
| WhiskRule(namespace, aname, afullname(namespace, "a trigger"), afullname(namespace, "an action")), |
| WhiskRule(namespace, aname, afullname(namespace, "a trigger"), afullname(namespace, "an action"))) |
| val docs = rules.map { entity => |
| putGetCheck(datastore, entity, WhiskRule) |
| } |
| } |
| |
| it should "CRD activation" in { |
| implicit val tid = transid() |
| implicit val basename = EntityName("create action blackbox") |
| val activations = Seq( |
| WhiskActivation(namespace, aname, Subject(), ActivationId(), start = Instant.now, end = Instant.now), |
| WhiskActivation(namespace, aname, Subject(), ActivationId(), start = Instant.now, end = Instant.now)) |
| val docs = activations.map { entity => |
| putGetCheck(datastore, entity, WhiskActivation) |
| } |
| } |
| |
| it should "CRD activation with utf8 characters" in { |
| implicit val tid = transid() |
| implicit val basename = EntityName("create action blackbox") |
| val activations = Seq( |
| WhiskActivation(namespace, aname, Subject(), ActivationId(), start = Instant.now, end = Instant.now, logs = ActivationLogs(Vector("Prote\u00EDna")))) |
| val docs = activations.map { entity => |
| putGetCheck(datastore, entity, WhiskActivation) |
| } |
| } |
| |
| it should "reject action with null arguments" in { |
| val name = EntityName("bad action") |
| intercept[IllegalArgumentException] { |
| WhiskAction(namespace, name, Exec.bb("i"), Parameters(), null) |
| } |
| } |
| |
| it should "reject trigger with null arguments" in { |
| val name = EntityName("bad trigger") |
| intercept[IllegalArgumentException] { |
| WhiskTrigger(namespace, name, Parameters(), null) |
| } |
| } |
| |
| it should "reject rule with null arguments" in { |
| val name = EntityName("bad rule") |
| intercept[IllegalArgumentException] { |
| WhiskRule(namespace, name, FullyQualifiedEntityName(namespace, EntityName(null)), FullyQualifiedEntityName(namespace, EntityName(null))) |
| } |
| intercept[IllegalArgumentException] { |
| WhiskRule(namespace, name, FullyQualifiedEntityName(namespace, EntityName("")), FullyQualifiedEntityName(namespace, EntityName(null))) |
| } |
| intercept[IllegalArgumentException] { |
| WhiskRule(namespace, name, FullyQualifiedEntityName(namespace, EntityName(" ")), FullyQualifiedEntityName(namespace, EntityName(null))) |
| } |
| } |
| |
| it should "update auth with a revision" in { |
| implicit val tid = transid() |
| val auth = WhiskAuth(Subject(), AuthKey()) |
| val docinfo = putGetCheck(authstore, auth, WhiskAuth, false)._2.docinfo |
| val revAuth = auth.revoke.revision[WhiskAuth](docinfo.rev) |
| putGetCheck(authstore, revAuth, WhiskAuth) |
| } |
| |
| it should "update action with a revision" in { |
| implicit val tid = transid() |
| implicit val basename = EntityName("update action") |
| val exec = Exec.js("update") |
| val action = WhiskAction(namespace, aname, exec, Parameters(), ActionLimits()) |
| val docinfo = putGetCheck(datastore, action, WhiskAction, false)._2.docinfo |
| val revAction = WhiskAction(namespace, action.name, exec, Parameters(), ActionLimits()).revision[WhiskAction](docinfo.rev) |
| putGetCheck(datastore, revAction, WhiskAction) |
| } |
| |
| it should "update trigger with a revision" in { |
| implicit val tid = transid() |
| implicit val basename = EntityName("update trigger") |
| val trigger = WhiskTrigger(namespace, aname) |
| val docinfo = putGetCheck(datastore, trigger, WhiskTrigger, false)._2.docinfo |
| val revTrigger = WhiskTrigger(namespace, trigger.name).revision[WhiskTrigger](docinfo.rev) |
| putGetCheck(datastore, revTrigger, WhiskTrigger) |
| } |
| |
| it should "update rule with a revision" in { |
| implicit val tid = transid() |
| implicit val basename = EntityName("update rule") |
| val rule = WhiskRule(namespace, aname, afullname(namespace, "a trigger"), afullname(namespace, "an action")) |
| val docinfo = putGetCheck(datastore, rule, WhiskRule, false)._2.docinfo |
| val revRule = WhiskRule(namespace, rule.name, rule.trigger, rule.action).revision[WhiskRule](docinfo.rev) |
| putGetCheck(datastore, revRule, WhiskRule) |
| } |
| |
| it should "update activation with a revision" in { |
| implicit val tid = transid() |
| implicit val basename = EntityName("update activation") |
| val activation = WhiskActivation(namespace, aname, Subject(), ActivationId(), start = Instant.now, end = Instant.now) |
| val docinfo = putGetCheck(datastore, activation, WhiskActivation, false)._2.docinfo |
| val revActivation = WhiskActivation(namespace, aname, activation.subject, activation.activationId, start = Instant.now, end = Instant.now).revision[WhiskActivation](docinfo.rev) |
| putGetCheck(datastore, revActivation, WhiskActivation) |
| } |
| |
| it should "fail with document conflict when trying to write the same auth twice without a revision" in { |
| implicit val tid = transid() |
| val auth = WhiskAuth(Subject(), AuthKey()) |
| putGetCheck(authstore, auth, WhiskAuth) |
| intercept[DocumentConflictException] { |
| putGetCheck(authstore, auth, WhiskAuth) |
| } |
| } |
| |
| it should "fail with document conflict when trying to write the same action twice without a revision" in { |
| implicit val tid = transid() |
| implicit val basename = EntityName("create action twice") |
| val exec = Exec.js("twice") |
| val action = WhiskAction(namespace, aname, exec) |
| putGetCheck(datastore, action, WhiskAction) |
| intercept[DocumentConflictException] { |
| putGetCheck(datastore, action, WhiskAction) |
| } |
| } |
| |
| it should "fail with document conflict when trying to write the same trigger twice without a revision" in { |
| implicit val tid = transid() |
| implicit val basename = EntityName("create trigger twice") |
| val trigger = WhiskTrigger(namespace, aname, Parameters("x", "y")) |
| putGetCheck(datastore, trigger, WhiskTrigger) |
| intercept[DocumentConflictException] { |
| putGetCheck(datastore, trigger, WhiskTrigger) |
| } |
| } |
| |
| it should "fail with document conflict when trying to write the same rule twice without a revision" in { |
| implicit val tid = transid() |
| implicit val basename = EntityName("create rule twice") |
| val rule = WhiskRule(namespace, aname, afullname(namespace, "a trigger"), afullname(namespace, "an action")) |
| putGetCheck(datastore, rule, WhiskRule) |
| intercept[DocumentConflictException] { |
| putGetCheck(datastore, rule, WhiskRule) |
| } |
| } |
| |
| it should "fail with document conflict when trying to write the same activation twice without a revision" in { |
| implicit val tid = transid() |
| implicit val basename = EntityName("create activation twice") |
| val activation = WhiskActivation(namespace, aname, Subject(), ActivationId(), start = Instant.now, end = Instant.now) |
| putGetCheck(datastore, activation, WhiskActivation) |
| intercept[DocumentConflictException] { |
| putGetCheck(datastore, activation, WhiskActivation) |
| } |
| } |
| |
| it should "fail with document does not exist when trying to delete the same auth twice" in { |
| implicit val tid = transid() |
| val auth = WhiskAuth(Subject(), AuthKey()) |
| val doc = putGetCheck(authstore, auth, WhiskAuth, false)._1 |
| assert(Await.result(WhiskAuth.del(authstore, doc), dbOpTimeout)) |
| intercept[NoDocumentException] { |
| assert(Await.result(WhiskAuth.del(authstore, doc), dbOpTimeout)) |
| assert(false) |
| } |
| } |
| |
| it should "fail with document does not exist when trying to delete the same action twice" in { |
| implicit val tid = transid() |
| implicit val basename = EntityName("delete action twice") |
| val exec = Exec.js("twice") |
| val action = WhiskAction(namespace, aname, exec) |
| val doc = putGetCheck(datastore, action, WhiskAction, false)._1 |
| assert(Await.result(WhiskAction.del(datastore, doc), dbOpTimeout)) |
| intercept[NoDocumentException] { |
| Await.result(WhiskAction.del(datastore, doc), dbOpTimeout) |
| assert(false) |
| } |
| } |
| |
| it should "fail with document does not exist when trying to delete the same trigger twice" in { |
| implicit val tid = transid() |
| implicit val basename = EntityName("delete trigger twice") |
| val trigger = WhiskTrigger(namespace, aname, Parameters("x", "y")) |
| val doc = putGetCheck(datastore, trigger, WhiskTrigger, false)._1 |
| assert(Await.result(WhiskTrigger.del(datastore, doc), dbOpTimeout)) |
| intercept[NoDocumentException] { |
| Await.result(WhiskTrigger.del(datastore, doc), dbOpTimeout) |
| assert(false) |
| } |
| } |
| |
| it should "fail with document does not exist when trying to delete the same rule twice" in { |
| implicit val tid = transid() |
| implicit val basename = EntityName("delete rule twice") |
| val rule = WhiskRule(namespace, aname, afullname(namespace, "a trigger"), afullname(namespace, "an action")) |
| val doc = putGetCheck(datastore, rule, WhiskRule, false)._1 |
| assert(Await.result(WhiskRule.del(datastore, doc), dbOpTimeout)) |
| intercept[NoDocumentException] { |
| Await.result(WhiskRule.del(datastore, doc), dbOpTimeout) |
| assert(false) |
| } |
| } |
| |
| it should "fail with document does not exist when trying to delete the same activation twice" in { |
| implicit val tid = transid() |
| implicit val basename = EntityName("delete activation twice") |
| val activation = WhiskActivation(namespace, aname, Subject(), ActivationId(), start = Instant.now, end = Instant.now) |
| val doc = putGetCheck(datastore, activation, WhiskActivation, false)._1 |
| assert(Await.result(WhiskActivation.del(datastore, doc), dbOpTimeout)) |
| intercept[NoDocumentException] { |
| Await.result(WhiskActivation.del(datastore, doc), dbOpTimeout) |
| assert(false) |
| } |
| } |
| |
| it should "fail to CRD with undefined argument" in { |
| implicit val tid = transid() |
| intercept[IllegalArgumentException] { |
| Await.result(WhiskAction.del(null, null), dbOpTimeout) |
| assert(false) |
| } |
| intercept[IllegalArgumentException] { |
| Await.result(WhiskAction.put(null, null), dbOpTimeout) |
| assert(false) |
| } |
| } |
| } |