Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / tests / projects.py @ dc1c2b45

History | View | Annotate | Download (33.1 kB)

1
# -*- coding: utf-8 -*-
2
# Copyright 2011, 2012, 2013 GRNET S.A. All rights reserved.
3
#
4
# Redistribution and use in source and binary forms, with or
5
# without modification, are permitted provided that the following
6
# conditions are met:
7
#
8
#   1. Redistributions of source code must retain the above
9
#      copyright notice, this list of conditions and the following
10
#      disclaimer.
11
#
12
#   2. Redistributions in binary form must reproduce the above
13
#      copyright notice, this list of conditions and the following
14
#      disclaimer in the documentation and/or other materials
15
#      provided with the distribution.
16
#
17
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
18
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
21
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
24
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
25
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28
# POSSIBILITY OF SUCH DAMAGE.
29
#
30
# The views and conclusions contained in the software and
31
# documentation are those of the authors and should not be
32
# interpreted as representing official policies, either expressed
33
# or implied, of GRNET S.A.
34

    
35
from astakos.im.tests.common import *
36

    
37

    
38
NotFound = type('NotFound', (), {})
39

    
40

    
41
def find(f, seq):
42
    for item in seq:
43
        if f(item):
44
            return item
45
    return NotFound
46

    
47

    
48
class ProjectAPITest(TestCase):
49

    
50
    def setUp(self):
51
        self.client = Client()
52
        component1 = Component.objects.create(name="comp1")
53
        register.add_service(component1, "σέρβις1", "type1", [])
54
        # custom service resources
55
        resource11 = {"name": u"σέρβις1.ρίσορς11",
56
                      "desc": u"ρίσορς11 desc",
57
                      "service_type": "type1",
58
                      "service_origin": u"σέρβις1",
59
                      "ui_visible": True}
60
        r, _ = register.add_resource(resource11)
61
        register.update_base_default(r, 100)
62
        resource12 = {"name": u"σέρβις1.resource12",
63
                      "desc": "resource12 desc",
64
                      "service_type": "type1",
65
                      "service_origin": u"σέρβις1",
66
                      "unit": "bytes"}
67
        r, _ = register.add_resource(resource12)
68
        register.update_base_default(r, 1024)
69

    
70
        # create user
71
        self.user1 = get_local_user("test@grnet.gr")
72
        self.user2 = get_local_user("test2@grnet.gr")
73
        self.user2.uuid = "uuid2"
74
        self.user2.save()
75
        self.user3 = get_local_user("test3@grnet.gr")
76

    
77
        astakos = Component.objects.create(name="astakos")
78
        register.add_service(astakos, "astakos_account", "account", [])
79
        # create another service
80
        pending_app = {"name": "astakos.pending_app",
81
                       "desc": "pend app desc",
82
                       "service_type": "account",
83
                       "service_origin": "astakos_account",
84
                       "ui_visible": False,
85
                       "api_visible": False}
86
        r, _ = register.add_resource(pending_app)
87
        register.update_base_default(r, 3)
88
        request = {"resources": {r.name: {"member_capacity": 3,
89
                                          "project_capacity": 3}}}
90
        functions.modify_projects_in_bulk(Q(is_base=True), request)
91

    
92
    def create(self, app, headers):
93
        dump = json.dumps(app)
94
        r = self.client.post(reverse("api_projects"), dump,
95
                             content_type="application/json", **headers)
96
        body = json.loads(r.content)
97
        return r.status_code, body
98

    
99
    def modify(self, app, project_id, headers):
100
        dump = json.dumps(app)
101
        kwargs = {"project_id": project_id}
102
        r = self.client.put(reverse("api_project", kwargs=kwargs), dump,
103
                             content_type="application/json", **headers)
104
        body = json.loads(r.content)
105
        return r.status_code, body
106

    
107
    def project_action(self, project_id, action, app_id=None, headers=None):
108
        action_data = {"reason": ""}
109
        if app_id is not None:
110
            action_data["app_id"] = app_id
111
        action = json.dumps({action: action_data})
112
        r = self.client.post(reverse("api_project_action",
113
                                     kwargs={"project_id": project_id}),
114
                             action, content_type="application/json",
115
                             **headers)
116
        return r.status_code
117

    
118
    def memb_action(self, memb_id, action, headers):
119
        action = json.dumps({action: "reason"})
120
        r = self.client.post(reverse("api_membership_action",
121
                                     kwargs={"memb_id": memb_id}), action,
122
                             content_type="application/json", **headers)
123
        return r.status_code
124

    
125
    def join(self, project_id, headers):
126
        action = {"join": {"project": project_id}}
127
        req = json.dumps(action)
128
        r = self.client.post(reverse("api_memberships"), req,
129
                             content_type="application/json", **headers)
130
        body = json.loads(r.content)
131
        return r.status_code, body
132

    
133
    def enroll(self, project_id, user, headers):
134
        action = {
135
            "enroll": {
136
                "project": project_id,
137
                "user": user.email,
138
            }
139
        }
140
        req = json.dumps(action)
141
        r = self.client.post(reverse("api_memberships"), req,
142
                             content_type="application/json", **headers)
143
        body = json.loads(r.content)
144
        return r.status_code, body
145

    
146
    @im_settings(PROJECT_ADMINS=["uuid2"])
147
    def test_projects(self):
148
        client = self.client
149
        h_owner = {"HTTP_X_AUTH_TOKEN": self.user1.auth_token}
150
        h_admin = {"HTTP_X_AUTH_TOKEN": self.user2.auth_token}
151
        h_plain = {"HTTP_X_AUTH_TOKEN": self.user3.auth_token}
152
        r = client.get(reverse("api_project", kwargs={"project_id": 1}))
153
        self.assertEqual(r.status_code, 401)
154

    
155
        r = client.get(reverse("api_project", kwargs={"project_id": 1}),
156
                       **h_owner)
157
        self.assertEqual(r.status_code, 404)
158
        r = client.get(reverse("api_membership", kwargs={"memb_id": 100}),
159
                       **h_owner)
160
        self.assertEqual(r.status_code, 404)
161

    
162
        status = self.memb_action(1, "accept", h_admin)
163
        self.assertEqual(status, 409)
164

    
165
        app1 = {"name": "test.pr",
166
                "description": u"δεσκρίπτιον",
167
                "end_date": "2013-5-5T20:20:20Z",
168
                "join_policy": "auto",
169
                "max_members": 5,
170
                "resources": {u"σέρβις1.ρίσορς11": {
171
                    "project_capacity": 1024,
172
                    "member_capacity": 512}}
173
                }
174

    
175
        status, body = self.modify(app1, 100, h_owner)
176
        self.assertEqual(status, 404)
177

    
178
        # Create
179
        status, body = self.create(app1, h_owner)
180
        self.assertEqual(status, 201)
181
        project_id = body["id"]
182
        app_id = body["application"]
183

    
184
        # Get project
185
        r = client.get(reverse("api_project",
186
                               kwargs={"project_id": project_id}),
187
                       **h_owner)
188
        self.assertEqual(r.status_code, 200)
189
        body = json.loads(r.content)
190
        self.assertEqual(body["id"], project_id)
191
        self.assertEqual(body["last_application"]["id"], app_id)
192
        self.assertEqual(body["last_application"]["state"], "pending")
193
        self.assertEqual(body["state"], "uninitialized")
194
        self.assertEqual(body["owner"], self.user1.uuid)
195
        self.assertEqual(body["description"], u"δεσκρίπτιον")
196

    
197
        # Approve forbidden
198
        status = self.project_action(project_id, "approve", app_id=app_id,
199
                                     headers=h_owner)
200
        self.assertEqual(status, 403)
201

    
202
        # Create another with the same name
203
        status, body = self.create(app1, h_owner)
204
        self.assertEqual(status, 201)
205
        project2_id = body["id"]
206
        project2_app_id = body["application"]
207

    
208
        # Create yet another, with different name
209
        app_p3 = copy.deepcopy(app1)
210
        app_p3["name"] = "new.pr"
211
        status, body = self.create(app_p3, h_owner)
212
        self.assertEqual(status, 201)
213
        project3_id = body["id"]
214
        project3_app_id = body["application"]
215

    
216
        # No more pending allowed
217
        status, body = self.create(app_p3, h_owner)
218
        self.assertEqual(status, 409)
219

    
220
        # Cancel
221
        status = self.project_action(project3_id, "cancel",
222
                                     app_id=project3_app_id, headers=h_owner)
223
        self.assertEqual(status, 200)
224

    
225
        # Get project
226
        r = client.get(reverse("api_project",
227
                               kwargs={"project_id": project3_id}),
228
                       **h_owner)
229
        body = json.loads(r.content)
230
        self.assertEqual(body["state"], "deleted")
231

    
232
        # Modify of uninitialized failed
233
        app2 = {"name": "test.pr",
234
                "start_date": "2013-5-5T20:20:20Z",
235
                "end_date": "2013-7-5T20:20:20Z",
236
                "join_policy": "moderated",
237
                "leave_policy": "auto",
238
                "max_members": 3,
239
                "resources": {u"σέρβις1.ρίσορς11": {
240
                    "project_capacity": 1024,
241
                    "member_capacity": 1024}}
242
                }
243
        status, body = self.modify(app2, project_id, h_owner)
244
        self.assertEqual(status, 409)
245

    
246
        # Create the project again
247
        status, body = self.create(app2, h_owner)
248
        self.assertEqual(status, 201)
249
        project_id = body["id"]
250
        app_id = body["application"]
251

    
252
        # Dismiss failed
253
        status = self.project_action(project_id, "dismiss", app_id,
254
                                     headers=h_owner)
255
        self.assertEqual(status, 409)
256

    
257
        # Deny
258
        status = self.project_action(project_id, "deny", app_id,
259
                                     headers=h_admin)
260
        self.assertEqual(status, 200)
261

    
262
        # Get project
263
        r = client.get(reverse("api_project",
264
                               kwargs={"project_id": project_id}),
265
                       **h_owner)
266
        body = json.loads(r.content)
267
        self.assertEqual(body["last_application"]["id"], app_id)
268
        self.assertEqual(body["last_application"]["state"], "denied")
269
        self.assertEqual(body["state"], "uninitialized")
270

    
271
        # Dismiss
272
        status = self.project_action(project_id, "dismiss", app_id,
273
                                     headers=h_owner)
274
        self.assertEqual(status, 200)
275

    
276
        # Get project
277
        r = client.get(reverse("api_project",
278
                               kwargs={"project_id": project_id}),
279
                       **h_owner)
280
        body = json.loads(r.content)
281
        self.assertEqual(body["last_application"]["id"], app_id)
282
        self.assertEqual(body["last_application"]["state"], "dismissed")
283
        self.assertEqual(body["state"], "deleted")
284

    
285
        # Create the project again
286
        status, body = self.create(app2, h_owner)
287
        self.assertEqual(status, 201)
288
        project_id = body["id"]
289
        app_id = body["application"]
290

    
291
        # Approve
292
        status = self.project_action(project_id, "approve", app_id,
293
                                     headers=h_admin)
294
        self.assertEqual(status, 200)
295

    
296
        # Check memberships
297
        r = client.get(reverse("api_memberships"), **h_plain)
298
        body = json.loads(r.content)
299
        self.assertEqual(len(body), 1)
300

    
301
        # Enroll
302
        status, body = self.enroll(project_id, self.user3, h_owner)
303
        self.assertEqual(status, 200)
304
        m_plain_id = body["id"]
305

    
306
        # Get project
307
        r = client.get(reverse("api_project",
308
                               kwargs={"project_id": project_id}),
309
                       **h_owner)
310
        body = json.loads(r.content)
311
        # Join
312
        status, body = self.join(project_id, h_owner)
313
        self.assertEqual(status, 200)
314
        memb_id = body["id"]
315

    
316
        # Check memberships
317
        r = client.get(reverse("api_memberships"), **h_plain)
318
        body = json.loads(r.content)
319
        self.assertEqual(len(body), 2)
320
        m = find(lambda m: m["project"] == project_id, body)
321
        self.assertNotEqual(m, NotFound)
322
        self.assertEqual(m["user"], self.user3.uuid)
323
        self.assertEqual(m["state"], "accepted")
324

    
325
        r = client.get(reverse("api_memberships"), **h_owner)
326
        body = json.loads(r.content)
327
        self.assertEqual(len(body), 3)
328

    
329
        # Check membership
330
        r = client.get(reverse("api_membership", kwargs={"memb_id": memb_id}),
331
                       **h_admin)
332
        m = json.loads(r.content)
333
        self.assertEqual(m["user"], self.user1.uuid)
334
        self.assertEqual(m["state"], "requested")
335
        self.assertEqual(sorted(m["allowed_actions"]),
336
                         ["accept", "cancel", "reject"])
337

    
338
        r = client.get(reverse("api_membership", kwargs={"memb_id": memb_id}),
339
                       **h_plain)
340
        self.assertEqual(r.status_code, 403)
341

    
342
        status = self.memb_action(memb_id, "leave", h_admin)
343
        self.assertEqual(status, 409)
344

    
345
        status = self.memb_action(memb_id, "cancel", h_owner)
346
        self.assertEqual(status, 200)
347

    
348
        status, body = self.join(project_id, h_owner)
349
        self.assertEqual(status, 200)
350
        self.assertEqual(memb_id, body["id"])
351

    
352
        status = self.memb_action(memb_id, "reject", h_owner)
353
        self.assertEqual(status, 200)
354

    
355
        status, body = self.join(project_id, h_owner)
356
        self.assertEqual(status, 200)
357
        self.assertEqual(memb_id, body["id"])
358

    
359
        status = self.memb_action(memb_id, "accept", h_owner)
360
        self.assertEqual(status, 200)
361

    
362
        # Enroll fails, already in
363
        status, body = self.enroll(project_id, self.user1, h_owner)
364
        self.assertEqual(status, 409)
365

    
366
        # Remove member
367
        status = self.memb_action(memb_id, "remove", h_owner)
368
        self.assertEqual(status, 200)
369

    
370
        # Enroll a removed member
371
        status, body = self.enroll(project_id, self.user1, h_owner)
372
        self.assertEqual(status, 200)
373

    
374
        # Remove member
375
        status = self.memb_action(memb_id, "remove", h_owner)
376
        self.assertEqual(status, 200)
377

    
378
        # Re-join
379
        status, body = self.join(project_id, h_owner)
380
        self.assertEqual(status, 200)
381
        self.assertEqual(memb_id, body["id"])
382

    
383
        # Enroll a requested member
384
        status, body = self.enroll(project_id, self.user1, h_owner)
385
        self.assertEqual(status, 200)
386

    
387
        # Enroll fails, already in
388
        status, body = self.enroll(project_id, self.user1, h_owner)
389
        self.assertEqual(status, 409)
390

    
391
        # Enroll fails, project does not exist
392
        status, body = self.enroll(-1, self.user1, h_owner)
393
        self.assertEqual(status, 409)
394

    
395
        # Get projects
396
        ## Simple user mode
397
        r = client.get(reverse("api_projects"), **h_plain)
398
        body = json.loads(r.content)
399
        self.assertEqual(len(body), 2)
400
        p = body[0]
401
        with assertRaises(KeyError):
402
            p["pending_application"]
403

    
404
        ## Owner mode
405
        filters = {"state": "active"}
406
        r = client.get(reverse("api_projects"), filters, **h_owner)
407
        body = json.loads(r.content)
408
        self.assertEqual(len(body), 2)
409

    
410
        filters = {"state": "deleted"}
411
        r = client.get(reverse("api_projects"), filters, **h_owner)
412
        body = json.loads(r.content)
413
        self.assertEqual(len(body), 2)
414

    
415
        filters = {"state": "uninitialized"}
416
        r = client.get(reverse("api_projects"), filters, **h_owner)
417
        body = json.loads(r.content)
418
        self.assertEqual(len(body), 2)
419

    
420
        filters = {"name": "test.pr"}
421
        r = client.get(reverse("api_projects"), filters, **h_owner)
422
        body = json.loads(r.content)
423
        self.assertEqual(len(body), 4)
424

    
425
        # Leave failed
426
        status = self.memb_action(m_plain_id, "leave", h_owner)
427
        self.assertEqual(status, 403)
428

    
429
        # Leave
430
        status = self.memb_action(m_plain_id, "leave", h_plain)
431
        self.assertEqual(status, 200)
432

    
433
        # Suspend failed
434
        status = self.project_action(project_id, "suspend", headers=h_owner)
435
        self.assertEqual(status, 403)
436

    
437
        # Unsuspend failed
438
        status = self.project_action(project_id, "unsuspend", headers=h_admin)
439
        self.assertEqual(status, 409)
440

    
441
        # Suspend
442
        status = self.project_action(project_id, "suspend", headers=h_admin)
443
        self.assertEqual(status, 200)
444

    
445
        # Cannot view project
446
        r = client.get(reverse("api_project",
447
                               kwargs={"project_id": project_id}), **h_plain)
448
        self.assertEqual(r.status_code, 403)
449

    
450
        # Unsuspend
451
        status = self.project_action(project_id, "unsuspend", headers=h_admin)
452
        self.assertEqual(status, 200)
453

    
454
        # Cannot approve, project with same name exists
455
        status = self.project_action(project2_id, "approve", project2_app_id,
456
                                     headers=h_admin)
457
        self.assertEqual(status, 409)
458

    
459
        # Terminate
460
        status = self.project_action(project_id, "terminate", headers=h_admin)
461
        self.assertEqual(status, 200)
462

    
463
        # Join failed
464
        status, _ = self.join(project_id, h_admin)
465
        self.assertEqual(status, 409)
466

    
467
        # Can approve now
468
        status = self.project_action(project2_id, "approve", project2_app_id,
469
                                     headers=h_admin)
470
        self.assertEqual(status, 200)
471

    
472
        # Join new project
473
        status, body = self.join(project2_id, h_plain)
474
        self.assertEqual(status, 200)
475
        m_project2 = body["id"]
476

    
477
        # Get memberships of project
478
        filters = {"project": project2_id}
479
        r = client.get(reverse("api_memberships"), filters, **h_owner)
480
        body = json.loads(r.content)
481
        self.assertEqual(len(body), 1)
482
        self.assertEqual(body[0]["id"], m_project2)
483

    
484
        # Remove member
485
        status = self.memb_action(m_project2, "remove", h_owner)
486
        self.assertEqual(status, 200)
487

    
488
        # Reinstate failed
489
        status = self.project_action(project_id, "reinstate", headers=h_admin)
490
        self.assertEqual(status, 409)
491

    
492
        # Rename
493
        app2_renamed = copy.deepcopy(app2)
494
        app2_renamed["name"] = "new.name"
495
        status, body = self.modify(app2_renamed, project_id, h_owner)
496
        self.assertEqual(status, 201)
497
        app2_renamed_id = body["application"]
498

    
499
        # Get project
500
        r = client.get(reverse("api_project",
501
                               kwargs={"project_id": project_id}), **h_owner)
502
        body = json.loads(r.content)
503
        self.assertEqual(body["last_application"]["id"], app2_renamed_id)
504
        self.assertEqual(body["state"], "terminated")
505
        assertIn("deactivation_date", body)
506
        self.assertEqual(body["last_application"]["state"], "pending")
507
        self.assertEqual(body["last_application"]["name"], "new.name")
508
        status = self.project_action(project_id, "approve", app2_renamed_id,
509
                                     headers=h_admin)
510
        self.assertEqual(r.status_code, 200)
511

    
512
        # Change homepage
513
        status, body = self.modify({"homepage": "new.page"},
514
                                   project_id, h_owner)
515
        self.assertEqual(status, 201)
516

    
517
        r = client.get(reverse("api_project",
518
                               kwargs={"project_id": project_id}), **h_owner)
519
        body = json.loads(r.content)
520
        self.assertEqual(body["homepage"], "")
521
        self.assertEqual(body["last_application"]["homepage"], "new.page")
522
        homepage_app = body["last_application"]["id"]
523
        status = self.project_action(project_id, "approve", homepage_app,
524
                                     headers=h_admin)
525
        self.assertEqual(r.status_code, 200)
526
        r = client.get(reverse("api_project",
527
                               kwargs={"project_id": project_id}), **h_owner)
528
        body = json.loads(r.content)
529
        self.assertEqual(body["homepage"], "new.page")
530

    
531
        # Bad requests
532
        r = client.head(reverse("api_projects"), **h_admin)
533
        self.assertEqual(r.status_code, 405)
534
        self.assertTrue('Allow' in r)
535

    
536
        r = client.head(reverse("api_project",
537
                                kwargs={"project_id": 1}), **h_admin)
538
        self.assertEqual(r.status_code, 405)
539
        self.assertTrue('Allow' in r)
540

    
541
        r = client.head(reverse("api_memberships"), **h_admin)
542
        self.assertEqual(r.status_code, 405)
543
        self.assertTrue('Allow' in r)
544

    
545
        status = self.project_action(1, "nonex", headers=h_owner)
546
        self.assertEqual(status, 400)
547

    
548
        action = json.dumps({"suspend": "", "unsuspend": ""})
549
        r = client.post(reverse("api_project_action",
550
                                kwargs={"project_id": 1}),
551
                        action, content_type="application/json", **h_owner)
552
        self.assertEqual(r.status_code, 400)
553

    
554
        ap = {"owner": "nonex",
555
              "join_policy": "nonex",
556
              "leave_policy": "nonex",
557
              "start_date": "nonex",
558
              "homepage": {},
559
              "max_members": -3,
560
              "resources": [],
561
              }
562

    
563
        status, body = self.create(ap, h_owner)
564
        self.assertEqual(status, 400)
565
        self.assertEqual(body["badRequest"]["message"], "User does not exist.")
566

    
567
        ap["owner"] = self.user1.uuid
568
        status, body = self.create(ap, h_owner)
569
        self.assertEqual(status, 400)
570

    
571
        ap["name"] = "some.name"
572
        status, body = self.create(ap, h_owner)
573
        self.assertEqual(status, 400)
574

    
575
        ap["join_policy"] = "auto"
576
        status, body = self.create(ap, h_owner)
577
        self.assertEqual(status, 400)
578

    
579
        ap["leave_policy"] = "closed"
580
        status, body = self.create(ap, h_owner)
581
        self.assertEqual(status, 400)
582

    
583
        ap["start_date"] = "2013-01-01T0:0Z"
584
        status, body = self.create(ap, h_owner)
585
        self.assertEqual(status, 400)
586

    
587
        ap["end_date"] = "2014-01-01T0:0Z"
588
        status, body = self.create(ap, h_owner)
589
        self.assertEqual(status, 400)
590

    
591
        ap["max_members"] = 0
592
        status, body = self.create(ap, h_owner)
593
        self.assertEqual(status, 400)
594

    
595
        ap["homepage"] = "a.stri.ng"
596
        status, body = self.create(ap, h_owner)
597
        self.assertEqual(status, 400)
598

    
599
        ap["resources"] = {42: 42}
600
        status, body = self.create(ap, h_owner)
601
        self.assertEqual(status, 400)
602

    
603
        ap["resources"] = {u"σέρβις1.ρίσορς11": {
604
                "member_capacity": 512}}
605
        status, body = self.create(ap, h_owner)
606
        self.assertEqual(status, 400)
607

    
608
        ap["resources"] = {u"σέρβις1.ρίσορς11": {"member_capacity": 512,
609
                                                 "project_capacity": 1024}}
610
        status, body = self.create(ap, h_owner)
611
        self.assertEqual(status, 201)
612

    
613
        ap["name"] = "non_domain_name"
614
        status, body = self.create(ap, h_owner)
615
        self.assertEqual(status, 400)
616

    
617
        ap["name"] = "domain.name"
618

    
619
        filters = {"state": "nonex"}
620
        r = client.get(reverse("api_projects"), filters, **h_owner)
621
        self.assertEqual(r.status_code, 400)
622

    
623
        r = self.client.post(reverse("api_projects"), "\xff",
624
                             content_type="application/json", **h_owner)
625
        self.assertEqual(r.status_code, 400)
626

    
627
        r = self.client.post(reverse("api_project_action",
628
                                     kwargs={"project_id": "1234"}),
629
                             "\"nondict\"", content_type="application/json",
630
                             **h_owner)
631
        self.assertEqual(r.status_code, 400)
632

    
633
        r = client.get(reverse("api_project",
634
                               kwargs={"project_id": u"πρότζεκτ"}),
635
                       **h_owner)
636
        self.assertEqual(r.status_code, 404)
637

    
638

    
639
class TestProjects(TestCase):
640
    """
641
    Test projects.
642
    """
643
    def setUp(self):
644
        # astakos resources
645
        self.resource = Resource.objects.create(name="astakos.pending_app",
646
                                                uplimit=0,
647
                                                project_default=0,
648
                                                ui_visible=False,
649
                                                api_visible=False,
650
                                                service_type="astakos")
651

    
652
        # custom service resources
653
        self.resource = Resource.objects.create(name="service1.resource",
654
                                                uplimit=100,
655
                                                project_default=0,
656
                                                service_type="service1")
657
        self.admin = get_local_user("projects-admin@synnefo.org")
658
        self.admin.uuid = 'uuid1'
659
        self.admin.save()
660

    
661
        self.user = get_local_user("user@synnefo.org")
662
        self.member = get_local_user("member@synnefo.org")
663
        self.member2 = get_local_user("member2@synnefo.org")
664

    
665
        self.admin_client = get_user_client("projects-admin@synnefo.org")
666
        self.user_client = get_user_client("user@synnefo.org")
667
        self.member_client = get_user_client("member@synnefo.org")
668
        self.member2_client = get_user_client("member2@synnefo.org")
669

    
670
    def tearDown(self):
671
        Service.objects.all().delete()
672
        ProjectApplication.objects.all().delete()
673
        Project.objects.all().delete()
674
        AstakosUser.objects.all().delete()
675

    
676
    @im_settings(PROJECT_ADMINS=['uuid1'])
677
    def test_application_limit(self):
678
        # user cannot create a project
679
        r = self.user_client.get(reverse('project_add'), follow=True)
680
        self.assertRedirects(r, reverse('project_list'))
681
        self.assertContains(r, "You are not allowed to create a new project")
682

    
683
        # but admin can
684
        r = self.admin_client.get(reverse('project_add'), follow=True)
685
        self.assertRedirects(r, reverse('project_add'))
686

    
687
    @im_settings(PROJECT_ADMINS=['uuid1'])
688
    def test_ui_visible(self):
689
        dfrom = datetime.now()
690
        dto = datetime.now() + timedelta(days=30)
691

    
692
        # astakos.pending_app ui_visible flag is False
693
        # we shouldn't be able to create a project application using this
694
        # resource.
695
        application_data = {
696
            'name': 'project.synnefo.org',
697
            'homepage': 'https://www.synnefo.org',
698
            'start_date': dfrom.strftime("%Y-%m-%d"),
699
            'end_date': dto.strftime("%Y-%m-%d"),
700
            'member_join_policy': 2,
701
            'member_leave_policy': 1,
702
            'limit_on_members_number': 5,
703
            'service1.resource_uplimit': 100,
704
            'is_selected_service1.resource': "1",
705
            'astakos.pending_app_uplimit': 100,
706
            'is_selected_accounts': "1",
707
            'user': self.user.pk
708
        }
709
        form = forms.ProjectApplicationForm(data=application_data)
710
        # form is invalid
711
        self.assertEqual(form.is_valid(), False)
712

    
713
        del application_data['astakos.pending_app_uplimit']
714
        del application_data['is_selected_accounts']
715
        form = forms.ProjectApplicationForm(data=application_data)
716
        self.assertEqual(form.is_valid(), True)
717

    
718
    @im_settings(PROJECT_ADMINS=['uuid1'])
719
    def test_applications(self):
720
        # let user have 2 pending applications
721

    
722
        # TODO figure this out
723
        request = {"resources": {"astakos.pending_app":
724
                                     {"member_capacity": 3,
725
                                      "project_capacity": 3}}}
726
        functions.modify_project(self.user.uuid, request)
727

    
728
        r = self.user_client.get(reverse('project_add'), follow=True)
729
        self.assertRedirects(r, reverse('project_add'))
730

    
731
        # user fills the project application form
732
        post_url = reverse('project_add') + '?verify=1'
733
        dfrom = datetime.now()
734
        dto = datetime.now() + timedelta(days=30)
735
        application_data = {
736
            'name': 'project.synnefo.org',
737
            'homepage': 'https://www.synnefo.org',
738
            'start_date': dfrom.strftime("%Y-%m-%d"),
739
            'end_date': dto.strftime("%Y-%m-%d"),
740
            'member_join_policy': 2,
741
            'member_leave_policy': 1,
742
            'service1.resource_uplimit': 100,
743
            'is_selected_service1.resource': "1",
744
            'user': self.user.pk
745
        }
746
        r = self.user_client.post(post_url, data=application_data, follow=True)
747
        self.assertEqual(r.status_code, 200)
748
        self.assertEqual(r.context['form'].is_valid(), False)
749

    
750
        application_data['limit_on_members_number'] = 5
751
        r = self.user_client.post(post_url, data=application_data, follow=True)
752
        self.assertEqual(r.status_code, 200)
753
        self.assertEqual(r.context['form'].is_valid(), True)
754

    
755
        # confirm request
756
        post_url = reverse('project_add') + '?verify=0&edit=0'
757
        r = self.user_client.post(post_url, data=application_data, follow=True)
758
        self.assertContains(r, "The project application has been received")
759
        self.assertRedirects(r, reverse('project_list'))
760
        self.assertEqual(ProjectApplication.objects.count(), 1)
761
        app1 = ProjectApplication.objects.filter().order_by('pk')[0]
762
        app1_id = app1.pk
763
        project1_id = app1.chain_id
764

    
765
        # create another one
766
        application_data['name'] = 'project2.synnefo.org'
767
        r = self.user_client.post(post_url, data=application_data, follow=True)
768
        app2 = ProjectApplication.objects.filter().order_by('pk')[1]
769
        project2_id = app2.chain_id
770

    
771
        # no more applications (LIMIT is 2)
772
        r = self.user_client.get(reverse('project_add'), follow=True)
773
        self.assertRedirects(r, reverse('project_list'))
774
        self.assertContains(r, "You are not allowed to create a new project")
775

    
776
        # one project per application
777
        self.assertEqual(Project.objects.count(), 2)
778

    
779
        # login
780
        self.admin_client.get(reverse("edit_profile"))
781
        # admin approves
782
        r = self.admin_client.post(reverse('project_app_approve',
783
                                           kwargs={'application_id': app1_id}),
784
                                   follow=True)
785
        self.assertEqual(r.status_code, 200)
786

    
787
        Q_ACTIVE = Project.o_state_q(Project.O_ACTIVE)
788
        self.assertEqual(Project.objects.filter(Q_ACTIVE).count(), 1)
789

    
790
        # login
791
        self.member_client.get(reverse("edit_profile"))
792
        # cannot join project2 (not approved yet)
793
        join_url = reverse("project_join", kwargs={'chain_id': project2_id})
794
        r = self.member_client.post(join_url, follow=True)
795

    
796
        # can join project1
797
        self.member_client.get(reverse("edit_profile"))
798
        join_url = reverse("project_join", kwargs={'chain_id': project1_id})
799
        r = self.member_client.post(join_url, follow=True)
800
        self.assertEqual(r.status_code, 200)
801

    
802
        memberships = ProjectMembership.objects.all()
803
        self.assertEqual(len(memberships), 1)
804
        memb_id = memberships[0].id
805

    
806
        reject_member_url = reverse('project_reject_member',
807
                                    kwargs={'memb_id': memb_id})
808
        accept_member_url = reverse('project_accept_member',
809
                                    kwargs={'memb_id': memb_id})
810

    
811
        # only project owner is allowed to reject
812
        r = self.member_client.post(reject_member_url, follow=True)
813
        self.assertContains(r, "You do not have the permissions")
814
        self.assertEqual(r.status_code, 200)
815

    
816
        # user (owns project) rejects membership
817
        r = self.user_client.post(reject_member_url, follow=True)
818
        self.assertEqual(ProjectMembership.objects.any_accepted().count(), 0)
819

    
820
        # user rejoins
821
        self.member_client.get(reverse("edit_profile"))
822
        join_url = reverse("project_join", kwargs={'chain_id': project1_id})
823
        r = self.member_client.post(join_url, follow=True)
824
        self.assertEqual(r.status_code, 200)
825
        self.assertEqual(ProjectMembership.objects.requested().count(), 1)
826

    
827
        # user (owns project) accepts membership
828
        r = self.user_client.post(accept_member_url, follow=True)
829
        self.assertEqual(ProjectMembership.objects.any_accepted().count(), 1)
830
        membership = ProjectMembership.objects.get()
831
        self.assertEqual(membership.state, ProjectMembership.ACCEPTED)
832

    
833
        user_quotas = quotas.get_users_quotas([self.member])
834
        resource = 'service1.resource'
835
        newlimit = user_quotas[self.member.uuid]['system'][resource]['limit']
836
        # 100 from initial uplimit + 100 from project
837
        self.assertEqual(newlimit, 200)
838

    
839
        remove_member_url = reverse('project_remove_member',
840
                                    kwargs={'memb_id': membership.id})
841
        r = self.user_client.post(remove_member_url, follow=True)
842
        self.assertEqual(r.status_code, 200)
843

    
844
        user_quotas = quotas.get_users_quotas([self.member])
845
        resource = 'service1.resource'
846
        newlimit = user_quotas[self.member.uuid]['system'][resource]['limit']
847
        # 200 - 100 from project
848
        self.assertEqual(newlimit, 100)
849

    
850
        # support email gets rendered in emails content
851
        for mail in get_mailbox('user@synnefo.org'):
852
            self.assertTrue(settings.CONTACT_EMAIL in
853
                            mail.message().as_string())