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  }