Merge branch 'master' of bitbucket.org:jamkazam/jam-web

This commit is contained in:
Seth Call 2012-12-02 08:23:44 -06:00
commit 999b8d72d0
25 changed files with 638 additions and 205 deletions

View File

@ -0,0 +1,21 @@
(function(context,$) {
context.JK = context.JK || {};
context.JK.FakeJamClient = function(app) {
var logger = context.JK.logger;
logger.info("*** Fake JamClient instance initialized. ***");
function testLatency(client, callback) {
var response = {
id: client.id,
latency: 50
};
callback(response);
}
// Javascript Bridge seems to camel-case
this.TestLatency = testLatency;
};
})(window,jQuery);

View File

@ -4,32 +4,73 @@
context.JK.FindSessionScreen = function(app) {
var logger = context.JK.logger;
var sessionLatency;
var sessions = {};
var selectors = {
TABLE_BODY: '#findSession-tableBody'
};
function loadSessions() {
$.ajax({
type: "GET",
url: "/api/sessions",
// TODO: Change to buildLatencyMap
success: renderSessions
success: startSessionLatencyChecks
});
}
function renderSessions(response) {
$tb = $('#findSession-tableBody');
$tb.empty();
var rowTemplate = $('#template-findSession-row').html();
$.each(response, function() {
var vals = {
id: this.id,
participants: this.participants.length,
description: this.description || "(No description)"
};
var row = JK.fillTemplate(rowTemplate, vals);
$tb.append(row);
function startSessionLatencyChecks(response) {
sessionLatency.subscribe(latencyResponse);
$.each(response, function(index, session) {
sessions[session.id] = session;
sessionLatency.sessionPings(session);
});
}
function latencyResponse(sessionId) {
renderSession(sessionId);
}
/**
* Not used normally. Allows modular unit testing
* of the renderSession method without having to do
* as much heavy setup.
*/
function setSession(session) { sessions[session.id] = session; }
/**
* Render a single session line into the table.
* It will be inserted at the appropriate place according to the
* sortScore in sessionLatency.
*/
function renderSession(sessionId) {
$tb = $(selectors.TABLE_BODY);
var rowTemplate = $('#template-findSession-row').html();
var session = sessions[sessionId];
var latencyInfo = sessionLatency.sessionInfo(sessionId);
var vals = {
id: session.id,
genres: session.genres.join (','),
sortScore: latencyInfo.sortScore,
participants: session.participants.length,
description: session.description || "(No description)"
};
var row = JK.fillTemplate(rowTemplate, vals);
var insertedEarly = false;
$.each($('tr', $tb), function() {
$this = $(this);
var rowSortScore = parseInt($this.attr('data-sortScore'), 10);
if (vals.sortScore > rowSortScore) {
$this.before(row);
insertedEarly = true;
}
});
if (!(insertedEarly)) {
return $tb.append(row);
}
}
function afterShow(data) {
$tb = $(selectors.TABLE_BODY);
$tb.empty();
loadSessions();
}
@ -47,20 +88,30 @@
$('#findSession-tableBody').on("click", '[action="delete"]', deleteSession);
}
this.afterShow = afterShow;
this.initialize = function() {
if ("jamClient" in context) {
sessionLatency = new context.JK.SessionLatency(jamClient);
/**
* Initialize, providing an instance of the SessionLatency class.
*/
function initialize(_sessionLatency) {
if (_sessionLatency) {
sessionLatency = _sessionLatency;
} else {
logger.warn("No jamClient available. Session pings will not be performed.");
logger.warn("No sessionLatency provided.");
}
screenBindings = {
'afterShow': afterShow
};
app.bindScreen('findSession', screenBindings);
events();
};
}
this.initialize = initialize;
this.afterShow = afterShow;
this.renderSession = renderSession;
// Following exposed for easier testing.
this.setSession = setSession;
this.selectors = selectors;
};
})(window,jQuery);

View File

@ -8,8 +8,43 @@
var sessionPingsOut = {};
var clientsToSessions = {};
var sessionLatency = {};
var subscribers = [];
function getSortScore(sessionId) {
return sessionLatency[sessionId].sortScore;
}
function ensureSessionLatencyEntry(sessionId) {
if (!(sessionId in sessionLatency)) {
sessionLatency[sessionId] = {
clientLatencies: {},
averageLatency: 0
};
}
}
function setInitialSortScore(session) {
var i,
p,
score = 0,
participant = null;
if ("invitations" in session) {
score += 2;
}
for (i=0, p=session.participants.length; i<p; i++) {
participant = session.participants[i];
if ("user_id" in participant) {
score += 1;
break;
}
}
ensureSessionLatencyEntry(session.id);
sessionLatency[session.id].sortScore = score;
}
function sessionPings(session) {
setInitialSortScore(session);
$.each(session.participants, function(index, participant) {
var client = { id: participant.client_id };
clientsToSessions[client.id] = session.id;
@ -17,7 +52,7 @@
sessionPingsOut[session.id] = 0;
}
sessionPingsOut[session.id]++;
jamClient.testLatency(client, clientPingResponse);
jamClient.TestLatency(client, clientPingResponse);
});
}
@ -25,18 +60,30 @@
var sessionId = clientsToSessions[response.id];
sessionPingsOut[sessionId]--;
updateSessionLatency(sessionId, response);
$.each(subscribers, function() {
this(sessionId);
});
}
function updateSessionLatency(sessionId, latencyResponse) {
if (!(sessionId in sessionLatency)) {
sessionLatency[sessionId] = {
clientLatencies: {},
averageLatency: 0
};
}
ensureSessionLatencyEntry(sessionId);
var sl = sessionLatency[sessionId];
sl.clientLatencies[latencyResponse.id] = latencyResponse.latency;
sl.averageLatency = latencyAverage(sl.clientLatencies);
sl.sortScore = updateSortScore(sessionId, sl.averageLatency);
}
function updateSortScore(sessionId, averageLatency) {
var newScore = sessionLatency[sessionId].sortScore;
if (!(newScore)) {
newScore = 0;
}
if (!averageLatency) {
return newScore;
}
var base = Math.floor(newScore);
newScore = base + (1/averageLatency);
return newScore;
}
function latencyAverage(clientLatencies) {
@ -53,8 +100,14 @@
return sessionLatency[sessionId];
}
function subscribe(cb) {
subscribers.push(cb);
}
this.sessionPings = sessionPings;
this.sessionInfo = sessionInfo;
this.getSortScore = getSortScore;
this.subscribe = subscribe;
return this;
};

View File

@ -64,17 +64,12 @@ class ApiBandsController < ApiController
end
if hide_private
@recordings = Recording.find(:all,
:joins => :musician_recordings,
:select => "recordings.id, recordings.description, recordings.public",
:conditions => ["bands_recordings.band_id='#{params[:id]}' AND public=true"])
@recordings = Recording.joins(:band_recordings)
.where(:bands_recordings => {:band_id => "#{params[:id]}"}, :public => true)
#.paginate(page: params[:page])
else
@recordings = Recording.find(:all,
:joins => :musician_recordings,
:select => "recordings.id, recordings.description, recordings.public",
:conditions => ["bands_recordings.band_id='#{params[:id]}'"])
@recordings = Recording.joins(:band_recordings)
.where(:bands_recordings => {:band_id => "#{params[:id]}"})
end
respond_with @recordings, responder: ApiResponder, :status => 200
@ -105,7 +100,8 @@ class ApiBandsController < ApiController
@recording = Recording.save(params[:recording_id],
params[:public],
params[:description],
params[:id],
params[:genres],
current_user.id,
params[:id],
true)
@ -116,6 +112,7 @@ class ApiBandsController < ApiController
@recording = Recording.save(params[:recording_id],
params[:public],
params[:description],
params[:genres],
current_user.id,
params[:id],
false)
@ -124,7 +121,7 @@ class ApiBandsController < ApiController
end
def recording_destroy
@band = Band.find_by_id(params[:id])
@band = Band.find(params[:id])
auth_band_member(@band, current_user)
@recording = Recording.find(params[:recording_id])

View File

@ -126,19 +126,15 @@ class ApiUsersController < ApiController
hide_private = true
end
select_list = "recordings.id, recordings.description, recordings.public, genres.id, genres.description"
if hide_private
@recordings = Recording.find(:all,
:joins => :musician_recordings,
:select => "recordings.id, recordings.description, recordings.public",
:conditions => ["musicians_recordings.user_id='#{params[:id]}' AND public=true"])
@recordings = Recording.joins(:musician_recordings)
.where(:musicians_recordings => {:user_id => "#{params[:id]}"}, :public => true)
#.paginate(page: params[:page])
else
@recordings = Recording.find(:all,
:joins => :musician_recordings,
:select => "recordings.id, recordings.description, recordings.public",
:conditions => ["musicians_recordings.user_id='#{params[:id]}'"])
@recordings = Recording.joins(:musician_recordings)
.where(:musicians_recordings => {:user_id => "#{params[:id]}"})
end
respond_with @recordings, responder: ApiResponder, :status => 200
@ -165,6 +161,7 @@ class ApiUsersController < ApiController
@recording = Recording.save(params[:recording_id],
params[:public],
params[:description],
params[:genres],
current_user.id,
params[:id],
false)
@ -177,6 +174,7 @@ class ApiUsersController < ApiController
@recording = Recording.save(params[:recording_id],
params[:public],
params[:description],
params[:genres],
current_user.id,
params[:id],
false)

View File

@ -39,14 +39,14 @@ class SessionsController < ApplicationController
# This should probably be in a transaction somehow, meaning the user
# create and the authorization create. Concern is UserManager.new.signup sends
# an email and whatnot.
user = UserManager.new.signup(auth_hash[:info][:first_name],
#
# Also, should we grab their photo from facebook?
user = UserManager.new.signup(request.remote_ip,
auth_hash[:info][:first_name],
auth_hash[:info][:last_name],
auth_hash[:info][:email],
nil,
nil,
nil, # city
nil, # state
nil, # country
nil, # instruments
nil, # photo_url
nil)

View File

@ -25,19 +25,16 @@ class UsersController < ApplicationController
render 'new'
return
end
@user = UserManager.new.signup(params[:jam_ruby_user][:first_name],
@user = UserManager.new.signup(request.remote_ip,
params[:jam_ruby_user][:first_name],
params[:jam_ruby_user][:last_name],
params[:jam_ruby_user][:email],
params[:jam_ruby_user][:password],
params[:jam_ruby_user][:password_confirmation],
params[:jam_ruby_user][:city],
params[:jam_ruby_user][:state],
params[:jam_ruby_user][:country],
params[:jam_ruby_user][:instruments],
params[:jam_ruby_user][:photo_url],
ApplicationHelper.base_uri(request) + "/confirm")
ApplicationHelper.base_uri(request) + "/confirm")
# check for errors
if @user.errors.any?

View File

@ -1,3 +1,11 @@
object @recordings
collection @recordings
extends "api_bands/recording_show"
attributes :id, :description, :public
node :genres do |recording|
unless recording.genres.nil? || recording.genres.size == 0
child :genres => :genres do
attributes :id, :description
end
end
end

View File

@ -1,3 +1,9 @@
object @recording
attributes :id, :description, :public
attributes :id, :description, :public
unless @recording.genres.nil? || @recording.genres.size == 0
child :genres => :genres do
attributes :id, :description
end
end

View File

@ -10,7 +10,7 @@ unless @band.users.nil? || @band.users.size == 0
node :instruments do |user|
unless user.instruments.nil? || user.instruments.size == 0
child :musician_instruments => :instruments do
attributes :id, :description, :proficiency_level, :priority
attributes :instrument_id, :description, :proficiency_level, :priority
end
end
end

View File

@ -1,3 +1,11 @@
object @recordings
collection @recordings
extends "api_users/recording_show"
attributes :id, :description, :public
node :genres do |recording|
unless recording.genres.nil? || recording.genres.size == 0
child :genres => :genres do
attributes :id, :description
end
end
end

View File

@ -1,3 +1,9 @@
object @recording
attributes :id, :description, :public
attributes :id, :description, :public
unless @recording.genres.nil? || @recording.genres.size == 0
child :genres => :genres do
attributes :id, :description
end
end

View File

@ -4,9 +4,13 @@
<p layout-link="home">Home</p>
<table>
<tr>
<th>Members</th>
<th>Name</th>
<th>Delete</th>
<th>Genre</th>
<th>Description</th>
<th>Musicians</th>
<th>Audience</th>
<th>Latency</th>
<th>Listen</th>
<th>Join</th>
</tr>
<tbody id="findSession-tableBody">
</tbody>
@ -14,9 +18,13 @@
</div>
<script type="text/template" id="template-findSession-row">
<tr>
<tr data-sortScore="{sortScore}">
<td>{genres}</td>
<td>{description}</td>
<td>{participants}</td>
<td><a href="#/session/{id}">{description}</a></td>
<td><a action="delete" action-id="{id}">X</a></td>
<td>TODO Audience</td>
<td>TODO Latency</td>
<td>TODO Listen</td>
<td><a href="#/session/{id}">Join</a></td>
</tr>
</script>

View File

@ -19,6 +19,11 @@
JK = JK || {};
// The following allows testing of native (jamClient) functionality
// with a fake. Uncomment when developing without the native client.
window.jamClient = new JK.FakeJamClient();
<% if current_user %>
JK.currentUserId = '<%= current_user.id %>';
<% else %>
@ -40,7 +45,11 @@
createSessionScreen.initialize();
var findSessionScreen = new JK.FindSessionScreen(jk);
findSessionScreen.initialize();
var sessionLatency = null;
if ("jamClient" in window) {
sessionLatency = new JK.SessionLatency(window.jamClient);
}
findSessionScreen.initialize(sessionLatency);
var sessionScreen = new JK.SessionScreen(jk);
sessionScreen.initialize();

View File

@ -82,5 +82,8 @@ module SampleApp
# if an inconsistency is detected'
config.elasticsearch_verify_mode = "autorepair"
# API key for filepicker.io gem
config.filepicker_rails.api_key = "AhUoVoBZSLirP3esyCl7Zz"
end
end

View File

@ -6,18 +6,21 @@ class MaxMindManager < BaseManager
# Returns a hash with location information. Fields are nil if they can't be figured.
# This is a class method because it doesn't need to be in a transaction.
def self.lookup(ip_address)
city = state = country = nil
ActiveRecord::Base.connection_pool.with_connection do |connection|
pg_conn = connection.instance_variable_get("@connection")
ip_as_int = ip_address_to_int(ip_address)
pg_conn.exec("SELECT country, region, city FROM max_mind WHERE ip_bottom <= $1 AND ip_top >= $2", [ip_as_int, ip_as_int]) do |result|
if !result.nil? && result.ntuples > 0
country = result.getvalue(0, 0)
state = result[0]['region']
city = result[0]['city']
unless ip_address.nil? || ip_address !~ /^\d+\.\d+\.\d+\.\d+$/
ActiveRecord::Base.connection_pool.with_connection do |connection|
pg_conn = connection.instance_variable_get("@connection")
ip_as_int = ip_address_to_int(ip_address)
pg_conn.exec("SELECT country, region, city FROM max_mind WHERE ip_bottom <= $1 AND ip_top >= $2", [ip_as_int, ip_as_int]) do |result|
if !result.nil? && result.ntuples > 0
country = result.getvalue(0, 0)
state = result[0]['region']
city = result[0]['city']
end
end
end
end

View File

@ -10,11 +10,13 @@ class UserManager < BaseManager
# Note that almost everything can be nil here. This is because when users sign up via social media,
# we don't know much about them.
def signup(first_name, last_name, email, password = nil, password_confirmation = nil,
city = nil, state = nil, country = nil, instruments = nil, photo_url = nil, signup_confirm_url = nil)
def signup(remote_ip, first_name, last_name, email, password = nil, password_confirmation = nil,
instruments = nil, photo_url = nil, signup_confirm_url = nil)
@user = User.new
location_hash = MaxMindManager.lookup(remote_ip);
# TODO: figure out why can't user verify_recaptcha here
# ALSO: make sure we dont do the recaptcha stuff if used facebook.
@ -22,9 +24,10 @@ class UserManager < BaseManager
#unless verify_recaptcha(:model => @user, :message => "recaptcha")
# return @user # @user.errors.any? is true now
#else
# sends email to email account for confirmation
@user = User.signup(first_name, last_name, email, password, password_confirmation,
city, state, country, instruments, photo_url, signup_confirm_url)
# sends email to email account for confirmation
@user = User.signup(first_name, last_name, email, password, password_confirmation,
location_hash[:city], location_hash[:state], location_hash[:country],
instruments, photo_url, signup_confirm_url)
return @user
#end

View File

@ -3,11 +3,6 @@
describe("FindSession", function() {
var fss;
var ajaxSpy;
var selectors = {
sessions: '#findSession-tableBody'
};
var appFake = {
clientId: '12345',
@ -16,34 +11,130 @@
ajaxError: function() { console.debug("ajaxError"); }
};
var jamClientFake = {
TestLatency: function(client, cb) {
cb({id: client.id, latency: 50});
}
};
beforeEach(function() {
fss = null;
// Use the actual screen markup
jasmine.getFixtures().fixturesPath = '/app/views/clients';
loadFixtures('_findSession.html.erb');
spyOn(appFake, 'notify');
fss = new context.JK.FindSessionScreen(appFake);
fss.initialize();
});
describe("loadSessions", function() {
beforeEach(function() {
spyOn($, "ajax");
});
it("should query ajax for sessions", function() {
fss.afterShow({});
expect($.ajax).toHaveBeenCalled();
});
});
var sessionLatencyReal;
describe("renderSessions", function() {
describe("RealSessionLatency", function() {
beforeEach(function() {
spyOn($, "ajax").andCallFake(function(opts) {
opts.success(TestResponses.getSessions);
sessionLatencyReal = new JK.SessionLatency(jamClientFake);
spyOn(sessionLatencyReal, 'sessionPings').andCallThrough();
fss = new context.JK.FindSessionScreen(appFake);
fss.initialize(sessionLatencyReal);
$(fss.selectors.TABLE_BODY).empty();
});
describe("loadSessions", function() {
beforeEach(function() {
spyOn($, "ajax");
});
it("should query ajax for sessions", function() {
fss.afterShow({});
expect($.ajax).toHaveBeenCalled();
});
});
it("should output table rows for sessions", function() {
fss.afterShow({});
expect($(selectors.sessions + ' tr').length).toEqual(1);
describe("afterShow flow", function() {
beforeEach(function() {
spyOn($, "ajax").andCallFake(function(opts) {
opts.success(TestGetSessionResponses.oneOfEach);
});
});
it("should output table rows for sessions", function() {
fss.afterShow({});
expect($(fss.selectors.TABLE_BODY + ' tr').length).toEqual(5);
});
it("should call sessionPings", function() {
fss.afterShow({});
expect(sessionLatencyReal.sessionPings).toHaveBeenCalled();
});
});
});
describe("FakeSessionLatency", function() {
beforeEach(function() {
sessionInfoResponses = {
"1": {id:"1", sortScore: 3},
"2": {id:"2", sortScore: 2}
};
sessionLatencyFake = {
sessionInfo: null
};
spyOn(sessionLatencyFake, 'sessionInfo').andCallFake(function(id) {
return sessionInfoResponses[id];
});
fss = new context.JK.FindSessionScreen(appFake);
fss.initialize(sessionLatencyFake);
$(fss.selectors.TABLE_BODY).empty();
});
describe("renderSession", function() {
describe("layout", function() {
var tbody;
beforeEach(function() {
var session = TestGetSessionResponses.oneOfEach[0];
fss.setSession(session);
fss.renderSession(session.id);
tbody = $(fss.selectors.TABLE_BODY);
});
it("single session should render", function() {
expect($('tr', tbody).length).toEqual(1);
});
it("Should render genre", function() {
expect($('tr td', tbody).first().text()).toEqual('classical');
});
it("Should render description", function() {
expect($('tr td', tbody).first().next().text()).toEqual('Invited');
});
it("Should render musician count", function() {
expect($('tr td', tbody).first().next().next().text()).toEqual('1');
});
// TODO - test audience
// TODO - test latency
// TODO - test Listen
it("Should render join link", function() {
expect($('tr td', tbody).last().text()).toEqual('Join');
});
});
it("higher sortScore inserted before lower sortScore", function() {
var sessionLow = TestGetSessionResponses.oneOfEach[1];
var sessionHigh = TestGetSessionResponses.oneOfEach[0];
fss.setSession(sessionLow);
fss.setSession(sessionHigh);
fss.renderSession(sessionLow.id);
fss.renderSession(sessionHigh.id);
var tbody = $(fss.selectors.TABLE_BODY);
expect($('tr', tbody).length).toEqual(2);
expect($('tr', tbody).first().attr('data-sortScore')).toEqual('3');
expect($('tr', tbody).first().next().attr('data-sortScore')).toEqual('2');
});
});
});

View File

@ -0,0 +1,120 @@
window.TestGetSessionResponses = {
oneOfEach: [
// Session 1 - you're invited to this.
{
"id": "1",
"description": "Invited",
"musician_access": true,
"genres" : [ "classical" ],
"participants": [
{
"client_id": "0f8f7987-29a0-4e5d-a60c-6b23103e3533",
"ip_address":"1.1.1.1",
"user_id" : "02303020402042040",
"tracks" : [
{
"id" : "xxxx",
"instrument_id" : "electric guitar",
"sound" : "mono"
}
]
}
],
"invitations" : [
{
"id" : "3948797598275987",
"sender_id" : "02303020402042040"
}
]
},
// Session 2 - no invite, but friend #1 (good latency)
{
"id": "2",
"description": "Friends 1",
"musician_access": true,
"genres" : [ "blues" ],
"participants": [
{
"client_id": "0f8f7987-29a0-4e5d-a60c-6b23103e3533",
"ip_address":"1.1.1.1",
"user_id" : "02303020402042040",
"tracks" : [
{
"id" : "xxxx",
"instrument_id" : "electric guitar",
"sound" : "mono"
}
]
}
]
},
// Session 3 - no invite, but friend #2 (med latency)
{
"id": "3",
"description": "Friends 2",
"musician_access": true,
"genres" : [ "blues" ],
"participants": [
{
"client_id": "0f8f7987-29a0-4e5d-a60c-6b23103e3533",
"ip_address":"1.1.1.1",
"user_id" : "02303020402042040",
"tracks" : [
{
"id" : "xxxx",
"instrument_id" : "electric guitar",
"sound" : "mono"
}
]
}
]
},
// Session 4 - no invite, no friends 1
{
"id": "4",
"description": "Anonymous 1",
"musician_access": true,
"genres" : [ "blues" ],
"participants": [
{
"client_id": "0f8f7987-29a0-4e5d-a60c-6b23103e3533",
"ip_address":"1.1.1.1",
"tracks" : [
{
"id" : "xxxx",
"instrument_id" : "electric guitar",
"sound" : "mono"
}
]
}
]
},
// Session 5 - no invite, no friends 2
{
"id": "5",
"description": "Anonymous 2",
"musician_access": true,
"genres" : [ "blues" ],
"participants": [
{
"client_id": "0f8f7987-29a0-4e5d-a60c-6b23103e3533",
"ip_address":"1.1.1.1",
"tracks" : [
{
"id" : "xxxx",
"instrument_id" : "electric guitar",
"sound" : "mono"
}
]
}
]
}
]
};

View File

@ -1,34 +1,4 @@
window.TestResponses = {
getSessions: [
{
"id": "1234",
"description": "Hello",
"musician_access": true,
"genres" : [ "classical" ],
"participants": [
{
"client_id": "0f8f7987-29a0-4e5d-a60c-6b23103e3533",
"ip_address":"1.1.1.1",
"user_id" : "02303020402042040", // NOTE THIS WILL BE UNDEFINED (ABSENT) IF THIS CLIENT IS NOT YOUR FRIEND
"tracks" : [
{
"id" : "xxxx",
"instrument_id" : "electric guitar",
"sound" : "mono"
}
]
}
],
"invitations" : [
{
"id" : "3948797598275987",
"sender_id" : "02303020402042040"
}
]
}
],
sessionPost: {
"id": "1234",
"description": "Hello",

View File

@ -5,55 +5,103 @@
var sessionLatency; // Instance of SessionLatency class for test
fakeJamClient = {
testLatency: function() {} // Will be overridden by test cases
TestLatency: function() {} // Will be overridden by test cases
};
var sessions = [
{id: "1", participants: [ { client_id: "1", ip_address: "1.1.1.1" } ] },
{id: "2", participants: [ { client_id: "2", ip_address: "1.1.1.2" } ] },
{id: "3", participants: [ { client_id: "3", ip_address: "1.1.1.3", user_id: "023" } ] },
{id: "4", participants: [ { client_id: "4", ip_address: "1.1.1.4" } ], invitations: [{id:'1', sender_id:'1'}] },
{id: "5", participants: [
{ client_id: "5", ip_address: "1.1.1.5" },
{ client_id: "6", ip_address: "1.1.1.6" },
{ client_id: "7", ip_address: "1.1.1.7" }
]}
];
var callCount = 0;
var testLatencyResponses = {
"1": {id: "1", latency: 35},
"2": {id: "2", latency: 50},
"3": {id: "3", latency: 150},
"4": {id: "4", latency: 200},
"5": {id: "5", latency: 100},
"6": {id: "6", latency: 10},
"7": {id: "7", latency: 10}
};
beforeEach(function() {
callCount = 0;
sessionLatency = new context.JK.SessionLatency(fakeJamClient);
spyOn(fakeJamClient, "TestLatency").andCallFake(function(client, callback) {
callback(testLatencyResponses[client.id]);
callCount++;
});
});
describe("SessionPings", function() {
it("should call jamClient.testLatency and compute new average", function() {
var session = {
id: "123",
participants: [ { client_id: "999", ip_address: "1.1.1.1" } ]
};
spyOn(fakeJamClient, "testLatency").andCallFake(function(client, callback) {
callback({id: "999", latency: 35});
});
sessionLatency.sessionPings(session);
expect(fakeJamClient.testLatency).toHaveBeenCalled();
var info = sessionLatency.sessionInfo(session.id);
it("should call jamClient.TestLatency and compute new average", function() {
sessionLatency.sessionPings(sessions[0]);
expect(fakeJamClient.TestLatency).toHaveBeenCalled();
var info = sessionLatency.sessionInfo(sessions[0].id);
expect(info.averageLatency).toEqual(35);
});
it("should average multiple client pings", function() {
var session = {
id: "123",
participants: [
{ client_id: "1", ip_address: "1.1.1.1" },
{ client_id: "2", ip_address: "1.1.1.2" },
{ client_id: "3", ip_address: "1.1.1.3" }
]
};
pingResponses = [
{id: "1", latency: 100},
{id: "2", latency: 10},
{id: "3", latency: 10}
];
pingResponseIndex = 0;
spyOn(fakeJamClient, "testLatency").andCallFake(function(client, callback) {
callback(pingResponses[pingResponseIndex]);
pingResponseIndex++;
});
sessionLatency.sessionPings(session);
var info = sessionLatency.sessionInfo(session.id);
sessionLatency.sessionPings(sessions[4]);
var info = sessionLatency.sessionInfo(sessions[4].id);
expect(info.averageLatency).toEqual(40);
});
});
describe("SessionSorting", function() {
beforeEach(function() {
$.each(sessions, function(index, session) {
sessionLatency.sessionPings(session);
});
});
it("should return >= 2 for invited sessions", function() {
var score = sessionLatency.getSortScore('4');
expect(score >= 2).toBeTruthy();
});
it("should return < 2, >= 1 for friend sessions", function() {
var score = sessionLatency.getSortScore('3');
expect(score < 2).toBeTruthy();
expect(score >= 1).toBeTruthy();
});
it("should return < 1 for unknown sessions", function() {
var score = sessionLatency.getSortScore('2');
expect(score < 1).toBeTruthy();
});
it("should return 1/AvgLatency for unknown pingable sessions", function() {
var score = sessionLatency.getSortScore('2');
expect(score).toEqual(0.02);
});
it("should return higher sort value for lower latency", function() {
var score1 = sessionLatency.getSortScore('1');
var score2 = sessionLatency.getSortScore('2');
expect(score1).toBeGreaterThan(score2);
});
});
describe("Register for Events", function() {
it("should register successfully", function() {
var cb = jasmine.createSpy();
sessionLatency.subscribe(cb);
});
it("should invoke callback on latency result", function() {
var cb = jasmine.createSpy("Latency Subscription Callback");
sessionLatency.subscribe(cb);
$.each(sessions, function(index, session) {
sessionLatency.sessionPings(session);
});
expect(cb).toHaveBeenCalled();
});
});
});

View File

@ -11,16 +11,16 @@ describe UserManager do
describe "signup" do
it "signup successfully" do
@user = @user_manager.signup("bob", "smith", "bob@jamkazam.com", "foobar", "foobar", "Austin", "TX", "USA", nil, nil, "http://localhost:3000/confirm" )
@user = @user_manager.signup("127.0.0.1", "bob", "smith", "bob@jamkazam.com", "foobar", "foobar", nil, nil, "http://localhost:3000/confirm" )
@user.errors.any?.should be_false
@user.first_name.should == "bob"
@user.last_name.should == "smith"
@user.email.should == "bob@jamkazam.com"
@user.email_confirmed.should be_false
@user.city.should == "Austin"
@user.state.should == "TX"
@user.country.should == "USA"
@user.city.should be_nil
@user.state.should be_nil
@user.country.should be_nil
@user.instruments.length.should == 0
@user.signup_token.should_not be_nil
@ -28,7 +28,7 @@ describe UserManager do
end
it "signup successfully with instruments" do
@user = @user_manager.signup("bob", "smith", "bob@jamkazam.com", "foobar", "foobar", "Austin", "TX", "USA",
@user = @user_manager.signup("127.0.0.1", "bob", "smith", "bob@jamkazam.com", "foobar", "foobar",
[{ :instrument_id => "electric guitar", :proficiency_level => 3, :priority => 0}], nil, "http://localhost:3000/confirm")
@user.errors.any?.should be_false
@ -38,36 +38,59 @@ describe UserManager do
musician_instrument.proficiency_level.should == 3
end
it "doesnt fail if ip address is nil" do
@user = @user_manager.signup(nil, "bob", "smith", "bob@jamkazam.com", "foobar", "foobar", nil, nil, "http://localhost:3000/confirm" )
@user.errors.any?.should be_false
@user.city.should be_nil
@user.state.should be_nil
@user.country.should be_nil
end
it "sets the location properly from maxmind" do
MaxMindManager.active_record_transaction do |manager|
manager.create_phony_database()
end
@user = @user_manager.signup("127.0.0.1", "bob", "smith", "bob@jamkazam.com", "foobar", "foobar", nil, nil, "http://localhost:3000/confirm" )
@user.errors.any?.should be_false
@user.city.should == 'City 127'
@user.state.should == 'Region 127'
@user.country.should == 'Country 127'
end
it "duplicate signup failure" do
@user = @user_manager.signup("bob", "smith", "bob@jamkazam.com", "foobar", "foobar", "Austin", "TX", "USA", nil, nil, "http://localhost:3000/confirm")
@user = @user_manager.signup("127.0.0.1", "bob", "smith", "bob@jamkazam.com", "foobar", "foobar", nil, nil, "http://localhost:3000/confirm")
UserMailer.deliveries.length.should == 1
@user.errors.any?.should be_false
# exactly the same parameters; should dup on email, and send no email
@user = @user_manager.signup("bob", "smith", "bob@jamkazam.com", "foobar", "foobar", "Austin", "TX", "USA", nil, nil, "http://localhost:3000/confirm")
@user = @user_manager.signup("127.0.0.1", "bob", "smith", "bob@jamkazam.com", "foobar", "foobar", nil, nil, "http://localhost:3000/confirm")
UserMailer.deliveries.length.should == 1
@user.errors.any?.should be_true
@user.errors[:email][0].should == "has already been taken"
end
it "fail on no username" do
@user = @user_manager.signup("", "", "bob@jamkazam.com", "foobar", "foobar", "Austin", "TX", "USA", nil, nil, "http://localhost:3000/confirm")
@user = @user_manager.signup("127.0.0.1", "", "", "bob@jamkazam.com", "foobar", "foobar", nil, nil, "http://localhost:3000/confirm")
UserMailer.deliveries.length.should == 0
@user.errors.any?.should be_true
@user.errors[:first_name][0].should == "can't be blank"
end
it "fail on no email" do
@user = @user_manager.signup("murp", "blurp", "", "foobar", "foobar", "Austin", "TX", "USA", nil, nil, "http://localhost:3000/confirm" )
@user = @user_manager.signup("127.0.0.1", "murp", "blurp", "", "foobar", "foobar", nil, nil, "http://localhost:3000/confirm" )
UserMailer.deliveries.length.should == 0
@user.errors.any?.should be_true
@user.errors[:email][0].should == "can't be blank"
end
end
describe "signup_confirm" do
it "fail on no username" do
@user = @user_manager.signup("bob", "smith", "bob@jamkazam.com", "foobar", "foobar", "Austin", "TX", "USA", nil, nil, "http://localhost:3000/confirm" )
@user = @user_manager.signup("127.0.0.1", "bob", "smith", "bob@jamkazam.com", "foobar", "foobar", nil, nil, "http://localhost:3000/confirm" )
@user = @user_manager.signup_confirm(@user.signup_token)
@user.email_confirmed.should be_true
end

View File

@ -55,8 +55,8 @@ describe "Band API", :type => :api do
end
########################## RECORDINGS #########################
def create_band_recording(authenticated_user, band_id, description, public)
post "/api/bands/#{band_id}/recordings.json", { :description => description, :public => public }.to_json, "CONTENT_TYPE" => 'application/json'
def create_band_recording(authenticated_user, band_id, description, public, genres)
post "/api/bands/#{band_id}/recordings.json", { :description => description, :public => public, :genres => genres }.to_json, "CONTENT_TYPE" => 'application/json'
return last_response
end
@ -246,6 +246,8 @@ describe "Band API", :type => :api do
recipient = FactoryGirl.create(:fan)
last_response = create_band_invitation(band.id, recipient.id)
last_response.status.should == 400
error_msg = JSON.parse(last_response.body)
error_msg["message"].should == BandInvitation::BAND_INVITATION_FAN_RECIPIENT_ERROR
# test receiver relationships
recipient.received_band_invitations.size.should == 0
@ -277,6 +279,13 @@ describe "Band API", :type => :api do
end
context "when logged in as user who is not in band A" do
before(:each) do
login(user.email, user.password, 200, true)
band.genres << Genre.find("hip hop")
band.genres << Genre.find("african")
band.genres << Genre.find("country")
end
it "should not allow user to update attributes of band A" do
end

View File

@ -117,9 +117,6 @@ describe "User pages" do
fill_in "Last Name", with: "User"
fill_in "Name", with: "Example User"
fill_in "Email", with: "user@example.com"
fill_in "City", with: "Austin"
fill_in "State", with: "TX"
fill_in "Country", with: "USA"
fill_in "Password", with: "foobar"
fill_in "Confirmation", with: "foobar"
end

View File

@ -66,15 +66,15 @@ describe "User API", :type => :api do
end
########################## RECORDINGS #########################
def create_user_recording(authenticated_user, source_user, description, public)
def create_user_recording(authenticated_user, source_user, description, public, genres)
login(authenticated_user.email, authenticated_user.password, 200, true)
post "/api/users/#{source_user.id}/recordings.json", { :description => description, :public => public }.to_json, "CONTENT_TYPE" => 'application/json'
post "/api/users/#{source_user.id}/recordings.json", { :description => description, :public => public, :genres => genres }.to_json, "CONTENT_TYPE" => 'application/json'
return last_response
end
def update_user_recording(authenticated_user, source_user, recording_id, description, public)
def update_user_recording(authenticated_user, source_user, recording_id, description, public, genres)
login(authenticated_user.email, authenticated_user.password, 200, true)
post "/api/users/#{source_user.id}/recordings/#{recording_id}.json", { :description => description, :public => public }.to_json, "CONTENT_TYPE" => 'application/json'
post "/api/users/#{source_user.id}/recordings/#{recording_id}.json", { :description => description, :public => public, :genres => genres }.to_json, "CONTENT_TYPE" => 'application/json'
return last_response
end
@ -294,7 +294,7 @@ describe "User API", :type => :api do
it "should allow musician to create recordings" do
# create public recording
public_description = "My Public Recording"
last_response = create_user_recording(user, user, public_description, true)
last_response = create_user_recording(user, user, public_description, true, ["african", "hip hop", "country"])
last_response.status.should == 201
recording = JSON.parse(last_response.body)
recording["description"].should == public_description
@ -302,14 +302,16 @@ describe "User API", :type => :api do
# create private recording
private_description = "My Private Recording"
last_response = create_user_recording(user, user, private_description, false)
last_response = create_user_recording(user, user, private_description, false, ["rock"])
last_response.status.should == 201
private_recording = JSON.parse(last_response.body)
private_recording["description"].should == private_description
private_recording["public"].should == false
private_recording["genres"].size.should == 1
private_recording["genres"][0]["id"].should == "rock"
# update the second recording's description and public flag
last_response = update_user_recording(user, user, private_recording["id"], "My Recording 3", true)
# update the second recording's description, public flag, and genre
last_response = update_user_recording(user, user, private_recording["id"], "My Recording 3", true, ["country", "hip hop"])
last_response.status.should == 200
recording = JSON.parse(last_response.body)
recording["description"].should == "My Recording 3"
@ -321,22 +323,24 @@ describe "User API", :type => :api do
recording = JSON.parse(last_response.body)
recording["description"].should == "My Recording 3"
recording["public"].should == true
recording["genres"].size.should == 2
end
it "should not allow fan to create recordings" do
last_response = create_user_recording(fan, fan, "Fan Recording", true)
last_response = create_user_recording(fan, fan, "Fan Recording", true, ["african", "hip hop", "country"])
last_response.status.should == 403
end
it "should allow creator to see public and private recordings in list" do
# create public recording
public_description = "My Public Recording"
last_response = create_user_recording(user, user, public_description, true)
last_response = create_user_recording(user, user, public_description, true, ["african", "hip hop", "country"])
last_response.status.should == 201
# create private recording
private_description = "My Private Recording"
last_response = create_user_recording(user, user, private_description, false)
last_response = create_user_recording(user, user, private_description, false, ["african", "hip hop"])
last_response.status.should == 201
# get all recordings as creator
@ -348,7 +352,7 @@ describe "User API", :type => :api do
it "should allow creator to see private recording details" do
# create private recording
private_description = "My Private Recording"
last_response = create_user_recording(user, user, private_description, false)
last_response = create_user_recording(user, user, private_description, false, ["african", "hip hop", "country"])
last_response.status.should == 201
private_recording = JSON.parse(last_response.body)
private_recording["description"].should == private_description
@ -362,12 +366,12 @@ describe "User API", :type => :api do
it "should not allow non-creator to see private recordings in list" do
# create public recording
public_description = "My Public Recording"
last_response = create_user_recording(user, user, public_description, true)
last_response = create_user_recording(user, user, public_description, true, ["african", "hip hop", "country"])
last_response.status.should == 201
# create private recording
private_description = "My Private Recording"
last_response = create_user_recording(user, user, private_description, false)
last_response = create_user_recording(user, user, private_description, false, ["african", "hip hop", "country"])
last_response.status.should == 201
# get all recordings as non-creator
@ -383,7 +387,7 @@ describe "User API", :type => :api do
it "should not allow non-creator to see private recording details" do
# create private recording
private_description = "My Private Recording"
last_response = create_user_recording(user, user, private_description, false)
last_response = create_user_recording(user, user, private_description, false, ["african", "hip hop", "country"])
last_response.status.should == 201
private_recording = JSON.parse(last_response.body)
private_recording["description"].should == private_description
@ -396,7 +400,7 @@ describe "User API", :type => :api do
it "should allow user to create favorites" do
# create recording first
last_response = create_user_recording(user, user, "My Recording", true)
last_response = create_user_recording(user, user, "My Recording", true, ["african", "hip hop", "country"])
last_response.status.should == 201
recording = JSON.parse(last_response.body)
@ -416,7 +420,7 @@ describe "User API", :type => :api do
it "should not allow user to create favorite for another user" do
# create recording first
last_response = create_user_recording(user, user, "My Recording", true)
last_response = create_user_recording(user, user, "My Recording", true, ["african", "hip hop", "country"])
last_response.status.should == 201
recording = JSON.parse(last_response.body)
@ -427,7 +431,7 @@ describe "User API", :type => :api do
it "should allow user to delete favorites" do
# create recording first
last_response = create_user_recording(user, user, "My Recording", true)
last_response = create_user_recording(user, user, "My Recording", true, ["african", "hip hop", "country"])
last_response.status.should == 201
recording = JSON.parse(last_response.body)
@ -444,7 +448,7 @@ describe "User API", :type => :api do
it "should not allow user to delete another user's favorites" do
# create recording first
last_response = create_user_recording(user, user, "My Recording", true)
last_response = create_user_recording(user, user, "My Recording", true, ["african", "hip hop", "country"])
last_response.status.should == 201
recording = JSON.parse(last_response.body)