diff --git a/resources/frontend_client/app/home/components/Activity.react.js b/resources/frontend_client/app/home/components/Activity.react.js
index aa254448e3a0e4544644348a54ff78034cc5d0c2..9626cfecd8dc67b4a244dd7d30a49a76d5cbc287 100644
--- a/resources/frontend_client/app/home/components/Activity.react.js
+++ b/resources/frontend_client/app/home/components/Activity.react.js
@@ -152,7 +152,7 @@ export default class Activity extends Component {
     }
 
     render() {
-        let { activity } = this.props;
+        let { activity, user } = this.props;
         let { error } = this.state;
 
         return (
@@ -172,7 +172,7 @@ export default class Activity extends Component {
                                     <li key={item.id} className="mt3">
                                         <ActivityItem
                                             item={item}
-                                            description={this.activityDescription(item, item.user)}
+                                            description={this.activityDescription(item, user)}
                                             userColors={this.initialsCssClasses(item.user)}
                                         />
                                         <ActivityStory story={this.activityDescription(item, item.user)} />
diff --git a/resources/frontend_client/app/home/components/Cards.react.js b/resources/frontend_client/app/home/components/Cards.react.js
index 761c5e0d75951c878fb5c8f745f892379b160179..4562963ca277f1e98b02bb86983c08e9ca58773b 100644
--- a/resources/frontend_client/app/home/components/Cards.react.js
+++ b/resources/frontend_client/app/home/components/Cards.react.js
@@ -24,6 +24,17 @@ export default class Cards extends Component {
         }
     }
 
+    tableName(table_id) {
+        const { databaseMetadata } = this.props;
+        for (var tableIdx in databaseMetadata.tables) {
+            if (databaseMetadata.tables[tableIdx].id === table_id) {
+                return databaseMetadata.tables[tableIdx].display_name;
+            }
+        }
+
+        return "";
+    }
+
     renderCards(cards) {
 
         let items = cards.slice().sort((a, b) => b.created_at - a.created_at);
@@ -59,7 +70,7 @@ export default class Cards extends Component {
     }
 
     render() {
-        let { cards } = this.props;
+        let { cards, cardsFilter, databaseMetadata } = this.props;
         let { error } = this.state;
 
         return (
@@ -69,8 +80,19 @@ export default class Cards extends Component {
                     { cards.length === 0 ?
                         <div className="flex flex-column layout-centered pt4" style={{fontSize: '1.08rem', marginTop: '100px'}}>
                             <span className="QuestionCircle">?</span>
-                            <div className="text-normal mt3 mb1 h2 text-bold">Hmmm, looks like you don't have any saved questions yet.</div>
-                            <div className="text-normal text-grey-2">Save a question and get this baby going!</div>
+                            <div className="text-normal mt3 mb1 h2 text-bold">
+                                { cardsFilter.database && cardsFilter.table ?
+                                    "No questions have been saved against "+this.tableName(cardsFilter.table)+" yet."
+                                : null}
+
+                                { cardsFilter.database && !cardsFilter.table ?
+                                    "No questions have been saved against "+databaseMetadata.name+" yet."
+                                : null}
+
+                                { !cardsFilter.database && !cardsFilter.table ?
+                                    "You don't have any saved questions yet."
+                                : null}
+                            </div>
                         </div>
                     :
                         this.renderCards(cards)
diff --git a/src/metabase/api/activity.clj b/src/metabase/api/activity.clj
index 4a72061d21fb420b842f01ee06b3679cd79cf02e..7ecdb74ff1257faf0a7c7a7bfb79f41e04a3217b 100644
--- a/src/metabase/api/activity.clj
+++ b/src/metabase/api/activity.clj
@@ -13,7 +13,7 @@
 (defendpoint GET "/"
   "Get recent activity."
   []
-  (-> (db/sel :many Activity (k/order :timestamp :DESC))
+  (-> (db/sel :many Activity (k/order :timestamp :DESC) (k/limit 40))
       (hydrate :user :table :database :model_exists)))
 
 (defendpoint GET "/recent_views"
@@ -30,7 +30,7 @@
              (k/where (= :user_id *current-user-id*))
              (k/group :user_id :model :model_id)
              (k/order :max_ts :desc)
-             (k/limit 15))
+             (k/limit 10))
            (map #(assoc % :model_object (delay (case (:model %)
                                          "card" (-> (Card (:model_id %))
                                                     (select-keys [:id :name :description :display]))
diff --git a/src/metabase/events/view_log.clj b/src/metabase/events/view_log.clj
index 693ee8a4f8114d07bc22795f262c9e6a349b227d..0bf3ab1348979206e7c9731362248ca329d2bf41 100644
--- a/src/metabase/events/view_log.clj
+++ b/src/metabase/events/view_log.clj
@@ -49,7 +49,7 @@
   [object]
   (or (:actor_id object) (:user_id object) (:creator_id object)))
 
-(defn- process-view-count-event
+(defn process-view-count-event
   "Handle processing for a single event notification received on the view-counts-channel"
   [event]
   ;; try/catch here to prevent individual topic processing exceptions from bubbling up.  better to handle them here.
diff --git a/src/metabase/models/user.clj b/src/metabase/models/user.clj
index 8cdd71c9b00786aa2a4c5121e2d65ea86e5a8850..ce14738436684a5af2bccc01a2e8c5615c962ad9 100644
--- a/src/metabase/models/user.clj
+++ b/src/metabase/models/user.clj
@@ -47,7 +47,10 @@
 
   (pre-cascade-delete [_ {:keys [id]}]
     (cascade-delete 'Session :user_id id)
-    (cascade-delete 'Activity :user_id id)))
+    (cascade-delete 'Dashboard :creator_id id)
+    (cascade-delete 'Card :creator_id id)
+    (cascade-delete 'Activity :user_id id)
+    (cascade-delete 'ViewLog :user_id id)))
 
 
 (def ^:const current-user-fields
diff --git a/test/metabase/api/activity_test.clj b/test/metabase/api/activity_test.clj
new file mode 100644
index 0000000000000000000000000000000000000000..70255c8b4ffc2afa4d7b1eab92094b77000bde25
--- /dev/null
+++ b/test/metabase/api/activity_test.clj
@@ -0,0 +1,167 @@
+(ns metabase.api.activity-test
+  "Tests for /api/activity endpoints."
+  (:require [expectations :refer :all]
+            [metabase.db :as db]
+            [metabase.http-client :refer :all]
+            (metabase.models [activity :refer [Activity]]
+                             [card :refer [Card]]
+                             [dashboard :refer [Dashboard]]
+                             [view-log :refer [ViewLog]])
+            [metabase.test.data :refer :all]
+            [metabase.test.data.users :refer :all]
+            [metabase.test.util :refer [match-$ expect-eval-actual-first random-name with-temp]]
+            [metabase.util :as u]))
+
+;; GET /
+
+; Things we are testing for:
+;  1. ordered by timestamp DESC
+;  2. :user and :model_exists are hydrated
+
+; NOTE: timestamp matching was being a real PITA so I cheated a bit.  ideally we'd fix that
+(expect-let [_         (korma.core/delete Activity)
+             activity1 (db/ins Activity
+                         :topic     "install"
+                         :details   {}
+                         :timestamp (u/parse-iso8601 "2015-09-09T12:13:14.888Z"))
+             activity2 (db/ins Activity
+                         :topic     "dashboard-create"
+                         :user_id   (user->id :crowberto)
+                         :model     "dashboard"
+                         :model_id  1234
+                         :details   {:description  "Because I can!"
+                                     :name         "Bwahahaha"
+                                     :public_perms 2}
+                         :timestamp (u/parse-iso8601 "2015-09-10T18:53:01.632Z"))
+             activity3 (db/ins Activity
+                         :topic     "user-joined"
+                         :user_id   (user->id :rasta)
+                         :model     "user"
+                         :details   {}
+                         :timestamp (u/parse-iso8601 "2015-09-10T05:33:43.641Z"))]
+  [(match-$ (db/sel :one Activity :id (:id activity2))
+     {:id           $
+      :topic        "dashboard-create"
+      :user_id      $
+      :user         (match-$ (fetch-user :crowberto)
+                      {:id           (user->id :crowberto)
+                       :email        $
+                       :date_joined  $
+                       :first_name   $
+                       :last_name    $
+                       :last_login   $
+                       :is_superuser $
+                       :common_name  $})
+      :model        $
+      :model_id     $
+      :model_exists false
+      :database_id  nil
+      :database     nil
+      :table_id     nil
+      :table        nil
+      :custom_id    nil
+      :details      $})
+   (match-$ (db/sel :one Activity :id (:id activity3))
+     {:id           $
+      :topic        "user-joined"
+      :user_id      $
+      :user         (match-$ (fetch-user :rasta)
+                      {:id           (user->id :rasta)
+                       :email        $
+                       :date_joined  $
+                       :first_name   $
+                       :last_name    $
+                       :last_login   $
+                       :is_superuser $
+                       :common_name  $})
+      :model        $
+      :model_id     $
+      :model_exists nil
+      :database_id  nil
+      :database     nil
+      :table_id     nil
+      :table        nil
+      :custom_id    nil
+      :details      $})
+   (match-$ (db/sel :one Activity :id (:id activity1))
+     {:id           $
+      :topic        "install"
+      :user_id      nil
+      :user         nil
+      :model        $
+      :model_id     $
+      :model_exists nil
+      :database_id  nil
+      :database     nil
+      :table_id     nil
+      :table        nil
+      :custom_id    nil
+      :details      $})]
+  (->> ((user->client :crowberto) :get 200 "activity")
+       (map #(dissoc % :timestamp))))
+
+
+;; GET /recent_views
+
+; Things we are testing for:
+;  1. ordering is sorted by most recent
+;  2. results are filtered to current user
+;  3. `:model_object` is hydrated in each result
+;  4. we filter out entries where `:model_object` is nil (object doesn't exist)
+
+(defn- create-card []
+  (db/ins Card
+    :name                   "rand-name"
+    :creator_id             (user->id :crowberto)
+    :public_perms           2
+    :display                "table"
+    :dataset_query          {}
+    :visualization_settings {}))
+
+(defn- create-dash []
+  (db/ins Dashboard
+    :name         "rand-name"
+    :description  "rand-name"
+    :creator_id   (user->id :crowberto)
+    :public_perms 2))
+
+(expect-let [card1 (create-card)
+             dash1 (create-dash)
+             card2 (create-card)]
+  [{:cnt      1
+    :user_id  (user->id :crowberto)
+    :model    "card"
+    :model_id (:id card1)
+    :model_object {:id          (:id card1)
+                   :name        (:name card1)
+                   :description (:description card1)
+                   :display     (name (:display card1))}}
+   {:cnt      1
+    :user_id  (user->id :crowberto)
+    :model    "dashboard"
+    :model_id (:id dash1)
+    :model_object {:id          (:id dash1)
+                   :name        (:name dash1)
+                   :description (:description dash1)}}
+   {:cnt      1
+    :user_id  (user->id :crowberto)
+    :model    "card"
+    :model_id (:id card2)
+    :model_object {:id          (:id card2)
+                   :name        (:name card2)
+                   :description (:description card2)
+                   :display     (name (:display card2))}}]
+  (let [create-view (fn [user model model-id]
+                      (db/ins ViewLog
+                        :user_id  user
+                        :model    model
+                        :model_id model-id
+                        :timestamp (u/new-sql-timestamp)))]
+    (do
+      (create-view (user->id :crowberto) "card" (:id card2))
+      (create-view (user->id :crowberto) "dashboard" (:id dash1))
+      (create-view (user->id :crowberto) "card" (:id card1))
+      (create-view (user->id :crowberto) "card" 36478)
+      (create-view (user->id :rasta) "card" (:id card1))
+      (->> ((user->client :crowberto) :get 200 "activity/recent_views")
+           (map #(dissoc % :max_ts))))))
diff --git a/test/metabase/events/activity_feed_test.clj b/test/metabase/events/activity_feed_test.clj
new file mode 100644
index 0000000000000000000000000000000000000000..d9d029de78607c13a0ebf1d258c9d98d0b68356a
--- /dev/null
+++ b/test/metabase/events/activity_feed_test.clj
@@ -0,0 +1,246 @@
+(ns metabase.events.activity-feed-test
+  (:require [expectations :refer :all]
+            [korma.core :as k]
+            [metabase.db :as db]
+            [metabase.events.activity-feed :refer :all]
+            (metabase.models [activity :refer [Activity]]
+                             [card :refer [Card]]
+                             [dashboard :refer [Dashboard]]
+                             [dashboard-card :refer [DashboardCard]]
+                             [database :refer [Database]]
+                             [session :refer [Session]]
+                             [user :refer [User]])
+            [metabase.test.data :refer :all]
+            [metabase.test.util :refer [expect-eval-actual-first with-temp random-name]]
+            [metabase.test-setup :refer :all]))
+
+;; TODO - we can simplify the cleanup work we do by using the :in-context :expectations-options
+;;        the only downside is that it then runs the annotated function on ALL tests :/
+
+
+(defn- create-test-objects
+  "Simple helper function which creates a series of test objects for use in the tests"
+  []
+  (let [rand-name (random-name)
+        user      (db/ins User
+                    :email      (str rand-name "@metabase.com")
+                    :first_name rand-name
+                    :last_name  rand-name
+                    :password   rand-name)
+        ;; i don't know why, but the below `ins` doesn't return an object :(
+        session   (db/ins Session
+                    :id      rand-name
+                    :user_id (:id user))
+        dashboard (db/ins Dashboard
+                    :name         rand-name
+                    :description  rand-name
+                    :creator_id   (:id user)
+                    :public_perms 2)
+        card      (db/ins Card
+                    :name                   rand-name
+                    :creator_id             (:id user)
+                    :public_perms           2
+                    :display                "table"
+                    :dataset_query          {:database (db-id)
+                                             :type     :query
+                                             :query    {:source_table (id :categories)}}
+                    :visualization_settings {})
+        dashcard  (db/ins DashboardCard
+                    :card_id      (:id card)
+                    :dashboard_id (:id dashboard))]
+    {:card      card
+     :dashboard dashboard
+     :dashcard  dashcard
+     :session   {:id rand-name}
+     :user      user}))
+
+
+;; `:card-create` event
+(expect-let [{:keys [card dashboard dashcard user]} (create-test-objects)]
+  {:topic       :card-create
+   :user_id     (:id user)
+   :model       "card"
+   :model_id    (:id card)
+   :database_id (db-id)
+   :table_id    (id :categories)
+   :details     {:description  (:description card)
+                 :name         (:name card)
+                 :public_perms (:public_perms card)}}
+  (do
+    (k/delete Activity)
+    (process-activity-event {:topic :card-create
+                             :item  card})
+    (-> (db/sel :one Activity :topic "card-create")
+        (select-keys [:topic :user_id :model :model_id :database_id :table_id :details]))))
+
+;; `:card-update` event
+(expect-let [{:keys [card dashboard dashcard user]} (create-test-objects)]
+  {:topic       :card-update
+   :user_id     (:id user)
+   :model       "card"
+   :model_id    (:id card)
+   :database_id (db-id)
+   :table_id    (id :categories)
+   :details     {:description  (:description card)
+                 :name         (:name card)
+                 :public_perms (:public_perms card)}}
+  (do
+    (k/delete Activity)
+    (process-activity-event {:topic :card-update
+                             :item  card})
+    (-> (db/sel :one Activity :topic "card-update")
+        (select-keys [:topic :user_id :model :model_id :database_id :table_id :details]))))
+
+;; `:card-delete` event
+(expect-let [{:keys [card dashboard dashcard user]} (create-test-objects)]
+  {:topic       :card-delete
+   :user_id     (:id user)
+   :model       "card"
+   :model_id    (:id card)
+   :database_id (db-id)
+   :table_id    (id :categories)
+   :details     {:description  (:description card)
+                 :name         (:name card)
+                 :public_perms (:public_perms card)}}
+  (do
+    (k/delete Activity)
+    (process-activity-event {:topic :card-delete
+                             :item  card})
+    (-> (db/sel :one Activity :topic "card-delete")
+        (select-keys [:topic :user_id :model :model_id :database_id :table_id :details]))))
+
+;; `:dashboard-create` event
+(expect-let [{:keys [card dashboard dashcard user]} (create-test-objects)]
+  {:topic       :dashboard-create
+   :user_id     (:id user)
+   :model       "dashboard"
+   :model_id    (:id dashboard)
+   :details     {:description  (:description dashboard)
+                 :name         (:name dashboard)
+                 :public_perms (:public_perms dashboard)}}
+  (do
+    (k/delete Activity)
+    (process-activity-event {:topic :dashboard-create
+                             :item  dashboard})
+    (-> (db/sel :one Activity :topic "dashboard-create")
+        (select-keys [:topic :user_id :model :model_id :details]))))
+
+;; `:dashboard-delete` event
+(expect-let [{:keys [card dashboard dashcard user]} (create-test-objects)]
+  {:topic       :dashboard-delete
+   :user_id     (:id user)
+   :model       "dashboard"
+   :model_id    (:id dashboard)
+   :details     {:description  (:description dashboard)
+                 :name         (:name dashboard)
+                 :public_perms (:public_perms dashboard)}}
+  (do
+    (k/delete Activity)
+    (process-activity-event {:topic :dashboard-delete
+                             :item  dashboard})
+    (-> (db/sel :one Activity :topic "dashboard-delete")
+        (select-keys [:topic :user_id :model :model_id :details]))))
+
+;; `:dashboard-add-cards` event
+(expect-let [{:keys [card dashboard dashcard user]} (create-test-objects)]
+  {:topic       :dashboard-add-cards
+   :user_id     (:id user)
+   :model       "dashboard"
+   :model_id    (:id dashboard)
+   :details     {:description  (:description dashboard)
+                 :name         (:name dashboard)
+                 :public_perms (:public_perms dashboard)
+                 :dashcards    [{:description  (:description card)
+                                 :name         (:name card)
+                                 :public_perms (:public_perms card)
+                                 :id           (:id dashcard)
+                                 :card_id      (:id card)}]}}
+  (do
+    (k/delete Activity)
+    (process-activity-event {:topic :dashboard-add-cards
+                             :item  {:id (:id dashboard) :actor_id (:id user) :dashcards [dashcard]}})
+    (-> (db/sel :one Activity :topic "dashboard-add-cards")
+        (select-keys [:topic :user_id :model :model_id :details]))))
+
+;; `:dashboard-remove-cards` event
+(expect-let [{:keys [card dashboard dashcard user]} (create-test-objects)]
+  {:topic       :dashboard-remove-cards
+   :user_id     (:id user)
+   :model       "dashboard"
+   :model_id    (:id dashboard)
+   :details     {:description  (:description dashboard)
+                 :name         (:name dashboard)
+                 :public_perms (:public_perms dashboard)
+                 :dashcards    [{:description  (:description card)
+                                 :name         (:name card)
+                                 :public_perms (:public_perms card)
+                                 :id           (:id dashcard)
+                                 :card_id      (:id card)}]}}
+  (do
+    (k/delete Activity)
+    (process-activity-event {:topic :dashboard-remove-cards
+                             :item  {:id (:id dashboard) :actor_id (:id user) :dashcards [dashcard]}})
+    (-> (db/sel :one Activity :topic "dashboard-remove-cards")
+        (select-keys [:topic :user_id :model :model_id :details]))))
+
+;; `:database-sync-*` events
+(expect
+  [1
+   {:topic       :database-sync
+    :user_id     nil
+    :model       "database"
+    :model_id    (db-id)
+    :database_id (db-id)
+    :custom_id   "abc"
+    :details     {:status "started"}}
+   {:topic       :database-sync
+    :user_id     nil
+    :model       "database"
+    :model_id    (db-id)
+    :database_id (db-id)
+    :custom_id   "abc"
+    :details     {:status "completed" :running_time 0}}]
+  (do
+    (k/delete Activity)
+    (let [_            (process-activity-event {:topic :database-sync-begin
+                                                :item  {:database_id (db-id) :custom_id "abc"}})
+          activity1    (-> (db/sel :one Activity :topic "database-sync")
+                           (select-keys [:topic :user_id :model :model_id :database_id :custom_id :details]))
+          _            (process-activity-event {:topic :database-sync-end
+                                                :item  {:database_id (db-id) :custom_id "abc"}})
+          activity2    (-> (db/sel :one Activity :topic "database-sync")
+                           (select-keys [:topic :user_id :model :model_id :database_id :custom_id :details])
+                           (assoc-in [:details :running_time] 0))
+          activity-cnt (:cnt (first (k/select Activity (k/aggregate (count :*) :cnt) (k/where {:topic "database-sync"}))))]
+      [activity-cnt
+       activity1
+       activity2])))
+
+;; `:install` event
+(expect
+  {:topic       :install
+   :user_id     nil
+   :model       "install"
+   :model_id    nil
+   :details     {}}
+  (do
+    (k/delete Activity)
+    (process-activity-event {:topic :install
+                             :item  {}})
+    (-> (db/sel :one Activity :topic "install")
+        (select-keys [:topic :user_id :model :model_id :details]))))
+
+;; `:user-login` event
+(expect-let [{{user-id :id} :user {session-id :id :as session} :session} (create-test-objects)]
+  {:topic       :user-joined
+   :user_id     user-id
+   :model       "user"
+   :model_id    user-id
+   :details     {}}
+  (do
+    (k/delete Activity)
+    (process-activity-event {:topic :user-login
+                             :item  {:user_id    user-id
+                                     :session_id session-id}})
+    (-> (db/sel :one Activity :topic "user-joined")
+        (select-keys [:topic :user_id :model :model_id :details]))))
diff --git a/test/metabase/events/view_log_test.clj b/test/metabase/events/view_log_test.clj
new file mode 100644
index 0000000000000000000000000000000000000000..a73564b3a53dac627853694f48333de8da90248e
--- /dev/null
+++ b/test/metabase/events/view_log_test.clj
@@ -0,0 +1,59 @@
+(ns metabase.events.view-log-test
+  (:require [expectations :refer :all]
+            [korma.core :as k]
+            [metabase.db :as db]
+            [metabase.events.view-log :refer :all]
+            (metabase.models [user :refer [User]]
+                             [view-log :refer [ViewLog]])
+            [metabase.test.data :refer :all]
+            [metabase.test.util :refer [expect-eval-actual-first with-temp random-name]]
+            [metabase.test-setup :refer :all]))
+
+
+(defn- create-test-user []
+  (let [rand-name (random-name)]
+    (db/ins User
+      :email      (str rand-name "@metabase.com")
+      :first_name rand-name
+      :last_name  rand-name
+      :password   rand-name)))
+
+
+;; `:card-create` event
+(expect-let [{user-id :id} (create-test-user)
+             card          {:id 1234
+                            :creator_id user-id}]
+  {:user_id     user-id
+   :model       "card"
+   :model_id    (:id card)}
+  (do
+    (process-view-count-event {:topic :card-create
+                               :item  card})
+    (-> (db/sel :one ViewLog :user_id user-id)
+        (select-keys [:user_id :model :model_id]))))
+
+;; `:card-read` event
+(expect-let [{user-id :id} (create-test-user)
+             card          {:id 1234
+                            :actor_id user-id}]
+  {:user_id     user-id
+   :model       "card"
+   :model_id    (:id card)}
+  (do
+    (process-view-count-event {:topic :card-read
+                               :item  card})
+    (-> (db/sel :one ViewLog :user_id user-id)
+        (select-keys [:user_id :model :model_id]))))
+
+;; `:dashboard-read` event
+(expect-let [{user-id :id} (create-test-user)
+             dashboard     {:id 1234
+                            :actor_id user-id}]
+  {:user_id     user-id
+   :model       "dashboard"
+   :model_id    (:id dashboard)}
+  (do
+    (process-view-count-event {:topic :dashboard-read
+                               :item  dashboard})
+    (-> (db/sel :one ViewLog :user_id user-id)
+        (select-keys [:user_id :model :model_id]))))