github.com/ergo-services/ergo@v1.999.224/tests/application_test.go (about) 1 package tests 2 3 import ( 4 "context" 5 "fmt" 6 "testing" 7 "time" 8 9 "github.com/ergo-services/ergo" 10 "github.com/ergo-services/ergo/etf" 11 "github.com/ergo-services/ergo/gen" 12 "github.com/ergo-services/ergo/lib" 13 "github.com/ergo-services/ergo/node" 14 ) 15 16 type testApplication struct { 17 gen.Application 18 } 19 20 func (a *testApplication) Load(args ...etf.Term) (gen.ApplicationSpec, error) { 21 lifeSpan := args[0].(time.Duration) 22 name := args[1].(string) 23 nameGS := "testAppGS" 24 if len(args) == 3 { 25 nameGS = args[2].(string) 26 } 27 return gen.ApplicationSpec{ 28 Name: name, 29 Description: "My Test Applicatoin", 30 Version: "v.0.1", 31 Env: map[gen.EnvKey]interface{}{ 32 "envName1": 123, 33 "envName2": "Hello world", 34 }, 35 Children: []gen.ApplicationChildSpec{ 36 { 37 Child: &testAppGenServer{}, 38 Name: nameGS, 39 }, 40 }, 41 Lifespan: lifeSpan, 42 }, nil 43 } 44 45 func (a *testApplication) Start(p gen.Process, args ...etf.Term) { 46 //p.SetEnv("env123", 456) 47 } 48 49 // test gen.Server 50 type testAppGenServer struct { 51 gen.Server 52 } 53 54 func (gs *testAppGenServer) Init(process *gen.ServerProcess, args ...etf.Term) error { 55 process.SetEnv("env123", 456) 56 return nil 57 } 58 59 func (gs *testAppGenServer) HandleCall(process *gen.ServerProcess, from gen.ServerFrom, message etf.Term) (etf.Term, gen.ServerStatus) { 60 return nil, gen.ServerStatusStop 61 } 62 63 // testing application 64 func TestApplicationBasics(t *testing.T) { 65 66 fmt.Printf("\n=== Test Application load/unload/start/stop\n") 67 fmt.Printf("\nStarting node nodeTestAplication@localhost:") 68 ctx := context.Background() 69 mynode, err := ergo.StartNodeWithContext(ctx, "nodeTestApplication@localhost", "cookies", node.Options{}) 70 if err != nil { 71 t.Fatal(err) 72 } else { 73 fmt.Println("OK") 74 } 75 76 app := &testApplication{} 77 lifeSpan := 0 * time.Second 78 79 // 80 // case 1: loading/unloading app 81 // 82 fmt.Printf("... loading application: ") 83 loaded, err := mynode.ApplicationLoad(app, lifeSpan, "testapp") 84 if err != nil { 85 t.Fatal(err) 86 } 87 if loaded != "testapp" { 88 t.Fatal("can't load application") 89 } 90 91 la := mynode.LoadedApplications() 92 93 // there are default applications - KernelApp, SystemApp thats why it 94 // should be equal 3. 95 if len(la) != 3 { 96 t.Fatal("total number of loaded application mismatch") 97 } 98 fmt.Println("OK") 99 100 wa := mynode.WhichApplications() 101 if len(wa) > 2 { 102 t.Fatal("total number of running application mismatch") 103 } 104 105 fmt.Printf("... unloading application: ") 106 if err := mynode.ApplicationUnload("testapp"); err != nil { 107 t.Fatal(err) 108 } 109 la = mynode.LoadedApplications() 110 if len(la) > 2 { 111 t.Fatal("total number of loaded application mismatch") 112 } 113 fmt.Println("OK") 114 115 // 116 // case 2: start(and try to unload running app)/stop(normal) application 117 // 118 fmt.Printf("... starting application: ") 119 if _, err := mynode.ApplicationLoad(app, lifeSpan, "testapp1", "testAppGS1"); err != nil { 120 t.Fatal(err) 121 } 122 123 p, e := mynode.ApplicationStart("testapp1") 124 if e != nil { 125 t.Fatal(e) 126 } 127 fmt.Println("OK") 128 129 fmt.Printf("... try to unload started application (shouldn't be able): ") 130 if e := mynode.ApplicationUnload("testapp1"); e != lib.ErrAppAlreadyStarted { 131 t.Fatal(e) 132 } 133 fmt.Println("OK") 134 135 fmt.Printf("... check total number of running applications (should be 3 including KernelApp, SystemApp): ") 136 wa = mynode.WhichApplications() 137 if n := len(wa); n != 3 { 138 t.Fatal(n) 139 } 140 fmt.Println("OK") 141 142 fmt.Printf("... check the name of running application (should be 'testapp1'): ") 143 found := false 144 for _, a := range wa { 145 if a.Name == "testapp1" { 146 found = true 147 break 148 } 149 150 } 151 if !found { 152 t.Fatal("can't find testapp1 among the running applications") 153 } 154 fmt.Println("OK") 155 156 // case 2.1: test env vars 157 fmt.Printf("... application's environment variables: ") 158 p.SetEnv("env123", 123) 159 p.SetEnv("envStr", "123") 160 161 gs := mynode.ProcessByName("testAppGS1") 162 if gs == nil { 163 t.Fatal("process testAppGS1 is not found by name") 164 } 165 env := gs.Env("env123") 166 if env == nil { 167 t.Fatal("incorrect environment variable: not found") 168 } 169 170 if i, ok := env.(int); !ok || i != 456 { 171 t.Fatal("incorrect environment variable: value should be overrided by child process") 172 } 173 174 if envUnknown := gs.Env("unknown"); envUnknown != nil { 175 t.Fatal("incorrect environment variable: undefined variable should have nil value") 176 } 177 178 envs := gs.ListEnv() 179 if x, ok := envs["env123"]; !ok || x != 456 { 180 t.Fatal("incorrect environment variable: list of variables has no env123 value or its wrong") 181 } 182 183 if x, ok := envs["envStr"]; !ok || x != "123" { 184 t.Fatal("incorrect environment variable: list of variables has no envStr value or its wrong") 185 } 186 187 fmt.Println("OK") 188 189 // case 2.2: get list of children' pid 190 fmt.Printf("... application's children list: ") 191 list, _ := p.Children() 192 if len(list) != 1 || list[0] != gs.Self() { 193 t.Fatal("incorrect children list") 194 } 195 fmt.Println("OK") 196 197 // case 2.3: get application info 198 199 fmt.Printf("... getting application info: ") 200 info, errInfo := mynode.ApplicationInfo("testapp1") 201 if errInfo != nil { 202 t.Fatal(errInfo) 203 } 204 if p.Self() != info.PID { 205 t.Fatal("incorrect pid in application info") 206 } 207 fmt.Println("OK") 208 209 fmt.Printf("... stopping application: ") 210 if e := mynode.ApplicationStop("testapp1"); e != nil { 211 t.Fatal(e) 212 } 213 fmt.Println("OK") 214 215 if e := p.WaitWithTimeout(100 * time.Millisecond); e != nil { 216 t.Fatal(e) 217 } 218 wa = mynode.WhichApplications() 219 if len(wa) != 2 { 220 fmt.Println("waa: ", wa) 221 t.Fatal("total number of running application mismatch") 222 } 223 224 // 225 // case 3: start/stop (brutal) application 226 // 227 fmt.Printf("... starting application for brutal kill: ") 228 if _, err := mynode.ApplicationLoad(app, lifeSpan, "testappBrutal", "testAppGS2Brutal"); err != nil { 229 t.Fatal(err) 230 } 231 p, e = mynode.ApplicationStart("testappBrutal") 232 if e != nil { 233 t.Fatal(e) 234 } 235 fmt.Println("OK") 236 fmt.Printf("... kill application: ") 237 p.Kill() 238 if e := p.WaitWithTimeout(100 * time.Millisecond); e != nil { 239 t.Fatal("timed out") 240 } 241 fmt.Println("OK") 242 243 mynode.ApplicationUnload("testappBrutal") 244 245 // 246 // case 4: start with limited lifespan 247 // 248 fmt.Printf("... starting application with lifespan 150ms: ") 249 lifeSpan = 150 * time.Millisecond 250 if _, err := mynode.ApplicationLoad(app, lifeSpan, "testapp2", "testAppGS2"); err != nil { 251 t.Fatal(err) 252 } 253 tStart := time.Now() 254 p, e = mynode.ApplicationStart("testapp2") 255 if e != nil { 256 t.Fatal(e) 257 } 258 // due to small lifespantiming it is ok to get real lifespan longer almost twice 259 if e := p.WaitWithTimeout(300 * time.Millisecond); e != nil { 260 t.Fatal("application lifespan was longer than 150ms") 261 } 262 fmt.Println("OK") 263 tLifeSpan := time.Since(tStart) 264 265 fmt.Printf("... application should be self stopped in 150ms: ") 266 if p.IsAlive() { 267 t.Fatal("still alive") 268 } 269 270 if tLifeSpan < lifeSpan { 271 t.Fatal("lifespan was shorter(", tLifeSpan, ") than ", lifeSpan) 272 } 273 274 fmt.Println("OK [ real lifespan:", tLifeSpan, "]") 275 276 mynode.Stop() 277 } 278 279 func TestApplicationTypePermanent(t *testing.T) { 280 fmt.Printf("\n=== Test Application type Permanent\n") 281 fmt.Printf("\nStarting node nodeTestAplicationPermanent@localhost:") 282 ctx := context.Background() 283 mynode, _ := ergo.StartNodeWithContext(ctx, "nodeTestApplicationPermanent@localhost", "cookies", node.Options{}) 284 if mynode == nil { 285 t.Fatal("can't start node") 286 } else { 287 fmt.Println("OK") 288 } 289 290 fmt.Printf("... starting application: ") 291 app := &testApplication{} 292 lifeSpan := time.Duration(0) 293 if _, err := mynode.ApplicationLoad(app, lifeSpan, "testapp"); err != nil { 294 t.Fatal(err) 295 } 296 297 p, e := mynode.ApplicationStartPermanent("testapp") 298 if e != nil { 299 t.Fatal(e) 300 } 301 fmt.Println("OK") 302 303 gs := mynode.ProcessByName("testAppGS") 304 if gs == nil { 305 t.Fatal("process testAppGS is not found by name") 306 } 307 fmt.Printf("... stop child with 'abnormal' reason: ") 308 gs.Exit("abnormal") 309 if e := gs.WaitWithTimeout(100 * time.Millisecond); e != nil { 310 t.Fatal("timeout on waiting child") 311 } 312 fmt.Println("OK") 313 314 if e := p.WaitWithTimeout(1 * time.Second); e != nil { 315 t.Fatal("timeout on waiting application stopping") 316 } 317 318 if e := mynode.WaitWithTimeout(1 * time.Second); e != nil { 319 t.Fatal("node shouldn't be alive here") 320 } 321 322 if mynode.IsAlive() { 323 t.Fatal("node shouldn't be alive here") 324 } 325 326 } 327 328 func TestApplicationTypeTransient(t *testing.T) { 329 fmt.Printf("\n=== Test Application type Transient\n") 330 fmt.Printf("\nStarting node nodeTestAplicationTypeTransient@localhost:") 331 ctx := context.Background() 332 mynode, _ := ergo.StartNodeWithContext(ctx, "nodeTestApplicationTypeTransient@localhost", "cookies", node.Options{}) 333 if mynode == nil { 334 t.Fatal("can't start node") 335 } else { 336 fmt.Println("OK") 337 } 338 339 app1 := &testApplication{} 340 app2 := &testApplication{} 341 lifeSpan := time.Duration(0) 342 343 if _, err := mynode.ApplicationLoad(app1, lifeSpan, "testapp1", "testAppGS1"); err != nil { 344 t.Fatal(err) 345 } 346 347 if _, err := mynode.ApplicationLoad(app2, lifeSpan, "testapp2", "testAppGS2"); err != nil { 348 t.Fatal(err) 349 } 350 351 fmt.Printf("... starting application testapp1: ") 352 p1, e1 := mynode.ApplicationStartTransient("testapp1") 353 if e1 != nil { 354 t.Fatal(e1) 355 } 356 fmt.Println("OK") 357 358 fmt.Printf("... starting application testapp2: ") 359 p2, e2 := mynode.ApplicationStartTransient("testapp2") 360 if e2 != nil { 361 t.Fatal(e2) 362 } 363 fmt.Println("OK") 364 365 fmt.Printf("... stopping testAppGS1 with 'normal' reason (shouldn't affect testAppGS2): ") 366 gs := mynode.ProcessByName("testAppGS1") 367 if gs == nil { 368 t.Fatal("process testAppGS1 is not found by name") 369 } 370 gs.Exit("normal") 371 if e := gs.WaitWithTimeout(100 * time.Millisecond); e != nil { 372 t.Fatal(e) 373 } 374 375 if e := p1.WaitWithTimeout(100 * time.Millisecond); e != lib.ErrTimeout { 376 t.Fatal("application testapp1 should be alive here") 377 } 378 379 fmt.Printf("... stopping application testapp1: ") 380 p1.Kill() 381 if e := p1.WaitWithTimeout(100 * time.Millisecond); e != nil { 382 t.Fatal("application testapp1 shouldn't be alive here:", e) 383 } 384 fmt.Println("OK") 385 386 p2.WaitWithTimeout(100 * time.Millisecond) 387 if !p2.IsAlive() { 388 t.Fatal("testAppGS2 should be alive here") 389 } 390 391 if !mynode.IsAlive() { 392 t.Fatal("node should be alive here") 393 } 394 395 fmt.Println("OK") 396 397 fmt.Printf("... starting application testapp1: ") 398 p1, e1 = mynode.ApplicationStartTransient("testapp1") 399 if e1 != nil { 400 t.Fatal(e1) 401 } 402 fmt.Println("OK") 403 404 fmt.Printf("... stopping testAppGS1 with 'abnormal' reason (node will shutdown): ") 405 gs = mynode.ProcessByName("testAppGS1") 406 gs.Exit("abnormal") 407 408 if e := gs.WaitWithTimeout(100 * time.Millisecond); e != nil { 409 t.Fatal(e) 410 } 411 412 if e := p1.WaitWithTimeout(100 * time.Millisecond); e != nil { 413 t.Fatal("testapp1 shouldn't be alive here") 414 } 415 416 if e := p2.WaitWithTimeout(100 * time.Millisecond); e != nil { 417 t.Fatal("testapp2 shouldn't be alive here") 418 } 419 420 if e := mynode.WaitWithTimeout(100 * time.Millisecond); e != nil { 421 t.Fatal("node shouldn't be alive here") 422 } 423 fmt.Println("OK") 424 } 425 426 func TestApplicationTypeTemporary(t *testing.T) { 427 fmt.Printf("\n=== Test Application type Temporary\n") 428 fmt.Printf("\nStarting node nodeTestAplicationStop@localhost:") 429 ctx := context.Background() 430 mynode, _ := ergo.StartNodeWithContext(ctx, "nodeTestApplicationStop@localhost", "cookies", node.Options{}) 431 if mynode == nil { 432 t.Fatal("can't start node") 433 } else { 434 fmt.Println("OK") 435 } 436 fmt.Printf("... starting application: ") 437 app := &testApplication{} 438 lifeSpan := time.Duration(0) 439 if _, err := mynode.ApplicationLoad(app, lifeSpan, "testapp"); err != nil { 440 t.Fatal(err) 441 } 442 443 _, e := mynode.ApplicationStart("testapp") // default start type is Temporary 444 if e != nil { 445 t.Fatal(e) 446 } 447 fmt.Println("OK") 448 449 fmt.Printf("... stopping testAppGS with 'normal' reason: ") 450 gs := mynode.ProcessByName("testAppGS") 451 if gs == nil { 452 t.Fatal("process testAppGS is not found by name") 453 } 454 gs.Exit("normal") 455 if e := gs.WaitWithTimeout(100 * time.Millisecond); e != nil { 456 t.Fatal(e) 457 } 458 459 if e := mynode.WaitWithTimeout(100 * time.Millisecond); e != lib.ErrTimeout { 460 t.Fatal("node should be alive here") 461 } 462 463 if !mynode.IsAlive() { 464 t.Fatal("node should be alive here") 465 } 466 fmt.Println("OK") 467 468 mynode.Stop() 469 } 470 471 func TestApplicationStop(t *testing.T) { 472 fmt.Printf("\n=== Test Application stopping\n") 473 fmt.Printf("\nStarting node nodeTestAplicationTypeTemporary@localhost:") 474 ctx := context.Background() 475 mynode, _ := ergo.StartNodeWithContext(ctx, "nodeTestApplicationTypeTemporary@localhost", "cookies", node.Options{}) 476 if mynode == nil { 477 t.Fatal("can't start node") 478 } else { 479 fmt.Println("OK") 480 } 481 fmt.Printf("... starting applications testapp1, testapp2: ") 482 lifeSpan := time.Duration(0) 483 app := &testApplication{} 484 if _, e := mynode.ApplicationLoad(app, lifeSpan, "testapp1", "testAppGS1"); e != nil { 485 t.Fatal(e) 486 } 487 488 app1 := &testApplication{} 489 if _, e := mynode.ApplicationLoad(app1, lifeSpan, "testapp2", "testAppGS2"); e != nil { 490 t.Fatal(e) 491 } 492 493 _, e1 := mynode.ApplicationStartPermanent("testapp1") 494 if e1 != nil { 495 t.Fatal(e1) 496 } 497 p2, e2 := mynode.ApplicationStartPermanent("testapp2") 498 if e2 != nil { 499 t.Fatal(e2) 500 } 501 fmt.Println("OK") 502 503 // case 1: stopping via node.ApplicatoinStop 504 fmt.Printf("... stopping testapp1 via node.ApplicationStop (shouldn't affect testapp2):") 505 if e := mynode.ApplicationStop("testapp1"); e != nil { 506 t.Fatal("can't stop application via node.ApplicationStop", e) 507 } 508 509 if !p2.IsAlive() { 510 t.Fatal("testapp2 should be alive here") 511 } 512 513 if !mynode.IsAlive() { 514 t.Fatal("node should be alive here") 515 } 516 517 fmt.Println("OK") 518 519 mynode.Stop() 520 521 }